diff options
Diffstat (limited to 'src/tt.output.c')
-rw-r--r-- | src/tt.output.c | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/src/tt.output.c b/src/tt.output.c new file mode 100644 index 0000000..b1d8e34 --- /dev/null +++ b/src/tt.output.c @@ -0,0 +1,199 @@ +// Outputting the results +// ============================================================================ + +// At this point, we have collected all references and accompanying insertinos +// in the source input. Two tasks remain: + +// 1. We need to parse the destination files, identifying <<destinations>>. +// 2. We need to copy the destination files to the tangled files, overwriting +// all <<destinations>> with the corresponding insertions. + +// Both of these tasks will be performed in the same loop: -> main.output + + for (k = offset; k < argc; k++) { + +// -> main.declarations + + int k; + +// The counter k is set to the offset defined in the options section, which +// should be equal to the position of the first destination file in argv. +// We loop as long as we haven't reached the end of argv. + +// On each iteration of the loop, we can obtain from argv the name of the +// destination file and copy it to a new string, adding the out_prefix. We'll +// call this string tangledfilename: -> main.declarations + + char *tangledfilename; + +// -> main.output + + tangledfilename = malloc(1 + (strlen(out_prefix) + strlen(argv[k]) + 50) * sizeof(char)); + if (tangledfilename == NULL) err(1, "malloc"); + + if (sprintf(tangledfilename, "%s%s", out_prefix, argv[k]) == -1) + err(1, "sprintf"); + +// Now, we can open the tangled file for writing and the original destination +// file for reading. We'll call the handle for tangledfile f and the handle for +// argv[k] fo, the o standing for "original": -> main.declarations + + FILE *f; + FILE *fo; + +// -> main.output + + f = fopen(tangledfilename, "w"); + if (f == NULL) err(1, "fopen"); + fo = fopen(argv[k], "r"); + if (fo == NULL) err(1, "fopen"); + +// Having successfully opened the files, we have no need for tangledfilename: +// -> main.output + + free(tangledfilename); + + +// Parsing the current destination file and writing the tangled file +// ---------------------------------------------------------------------------- + +// The destination file will be parsed in a manner similar to the way in which +// the source input was parsed. The same structure will be used: -> main.output + + line_l = 0; + /* line_s is remembered */ + + while ((b = fgetc(fo)) != EOF) { + c = b; + if (c != '\n') { + if (line_l + 1 > line_s) { + line_s += 20; + tmp = realloc(line, 1 + line_s * sizeof(char)); + if (tmp == NULL) err(1, "malloc"); + line = tmp; + } + line[line_l++] = c; + continue; + } + +// Again, characters will be added to the line variable until a newline is +// encountered, at which point the collected line will be finished: +// -> main.output + +finish2: + line[line_l] = '\0'; + line_l = 0; /* reset line length count */ + +// From here on, however, the loop will look a bit different. First, tt takes +// note of the line's indentation, saving it to the indent variable: +// -> main.declarations + + int indent; + +// Only spaces are currently supported: -> main.output + + ref = line; + for (indent = 0; *ref == ' '; ref++) indent++; + +// Also, as you can see, we re-use the ref variable that was used by the input +// parsing, but which is now unused. + +// Parsing the <<destination identifier>> is simple: -> main.output + + if (strncmp(ref, "<<", 2) != 0 + || strncmp(ref + strlen(ref) - 2, ">>", 2) != 0) { + fprintf(f, "%s\n", line); + continue; + } + +// If no potential destination is found, then the line will be written as-is to +// the tangled file, and the loop continues parsing the next line of the file. +// If a potential destination is found, however, we store it in the ref +// variable, removing the << and >> markers: -> main.output + + ref += 2; + ref[strlen(ref) - 2] = '\0'; + +// There is still one thing to check, before we know that the destination is +// valid -- it must not contain any whitespace: -> main.output + + for (i = 0; i < strlen(ref); i++) + if (isspace(ref[i])) { + fprintf(f, "%s\n", line); + continue; + } + +// Again, if there is whitespace, then the line does not signify a destination +// and should be printed as-is to the resulting tangled file. + +// As when parsing the input, long identifiers are truncated: -> main.output + + if (strlen(ref) > REFMAX) + fprintf(stderr, + "Warning: Truncating identifier exceeding %d characters\n", REFMAX); + +// Finally, we check whether the destination actually has been referenced by +// the source input, warning the user otherwise: -> main.output + + for (i = 0; i < refs_c; i++) + if (strncmp(refs[i], ref, REFMAX) == 0) goto found; + fprintf(stderr, "Unreferenced destination: %s\n", ref); + continue; +found: + +// Having established that the identified destination is referenced by the +// source input, and having stored in the local i variable the reference's +// position in the refs variable, we can retrieve the insertion for the +// reference by looking at the same position in the ins variable. + +// Our first order of business is to make sure that the insertion is not empty +// -- in that case, the user is warned, and the loop goes on to the next line: +// -> main.output + + if (ins[i] == NULL) { + fprintf(stderr, "Warning: Insertion for %s is empty\n", ref); + continue; + } + +// Now, we are ready to write the insertion for the destination to the tangled +// file. Because each insertion is stored as an array of strings, each string +// containing a single line of the insertion, we use yet another loop: +// -> main.output + + for (j = 0; ins[i][j] != NULL; j++) { + if (ins[i][j + 1] == NULL) { + if (strlen(ins[i][j]) == 0) + break; /* remove extra newline */ + } + for (m = indent; m > 0; m--) putc(' ', f); + fprintf(f, "%s\n", ins[i][j]); + } + } + +// -> main.declarations + +int j; +int m; + +// Apart from simply printing the inserted line to the tangled file, the code +// above also skips any empty line at the end of the insertion and adds the +// indentation identified when parsing the line in the destination file +// containing the destination identifier. + +// Now, we have almost finished parsing the current destination file and +// writing to the corresponding tangled file, but -- as before -- we still +// haven't processed the final line of the file, if that line ends without +// a newline. To fix that, we just run the finishing code again: +// -> main.output + + if (c != '\n') { c = '\n'; goto finish2; } + +// Finally, we close the handles to the destination file and tangled file: +// -> main.output + + fclose(f); + fclose(fo); + } + +// And that is the end of the loop. The loop continues for every destination +// file given as an argument, and when it is done, so is the program.
\ No newline at end of file |