// 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 <>. // 2. We need to copy the destination files to the tangled files, overwriting // all <> 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 == '\r') continue; 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 <> 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.