aboutsummaryrefslogtreecommitdiff
path: root/src/tt.output.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tt.output.c')
-rw-r--r--src/tt.output.c199
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