// tt.c -- tangle to, written by John Ankarström -> tt.c #include #ifdef _WIN32 #include #pragma comment(lib, "Shlwapi.lib") #else #include #include #include #include #include #include #endif #define REFMAX 80 #define USAGE "usage: %s [-cCODE_PREFIX] [-dDOC_PREFIX] [-oOUTPREFIX] destination ...\n", argv[0] #define err(code, string) do { fprintf(stderr, "%s: %s: %s\n", string, strerror(errno)); exit(code); } while (0) #define die(...) do { fprintf(stderr, __VA_ARGS__); exit(1); } while (0) #define true 1 #define false 0 #define bool int char **refs; /* references */ int refs_c; /* count */ int refs_s; /* size (number of elements allocated for) */ char ***ins; /* insertions */ void reference(char *line); char *ref; bool insertion(char *line); char *code_prefix; /* string with which code lines should start */ char *doc_prefix; /* string with which documentation lines should start */ char *out_prefix; /* string to which the output file name should be appended */ int main(int argc, char *argv[]) { int i; char *line; int line_l; /* length */ int line_s; /* size (number of characters allocated for) */ int b; int c; char *tmp; bool wascode = false; int offset; int k; char *tangledfilename; FILE *f; FILE *fo; int indent; int j; int m; refs_c = 0; refs_s = 10; refs = malloc(refs_s * sizeof(char *)); if (refs == NULL) err(1, "malloc"); ins = malloc(refs_s * sizeof(char **)); if (ins == NULL) err(1, "malloc"); for (i = 0; i < refs_s; i++) ins[i] = NULL; ref = malloc(1 + REFMAX * sizeof(char)); if (ref == NULL) err(1, "malloc"); code_prefix = " "; /* code lines should begin with four spaces */ doc_prefix = ""; /* other lines are documentation lines */ out_prefix = "out/"; /* all output files go in the out/ directory */ for (i = 1; i < argc; i++) if (argv[i][0] == '-') { switch(argv[i][1]) { case 'c': code_prefix = argv[i] + 2; break; case 'd': doc_prefix = argv[i] + 2; break; case 'o': out_prefix = argv[i] + 2; break; case '-': i++; goto end; default: die(USAGE); } } else break; end: if (strcmp(code_prefix, doc_prefix) == 0) die("code_prefix and doc_prefix cannot be identical\n"); if (strlen(out_prefix) == 0) die("out_prefix cannot be empty\n"); if (i == argc) die(USAGE); offset = i; line_l = 0; line_s = 100; line = malloc(1 + line_s * sizeof(char)); if (line == NULL) err(1, "malloc"); while ((b = getchar()) != 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; } finish: line[line_l] = '\0'; line_l = 0; if (strlen(code_prefix) == 0 && !wascode && strcmp(line, "") == 0) { continue; } if (!insertion(line)) reference(line); } if (c != '\n') { c = '\n'; goto finish; } free(ref); for (k = offset; k < argc; k++) { 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"); f = fopen(tangledfilename, "w"); if (f == NULL) err(1, "fopen"); fo = fopen(argv[k], "r"); if (fo == NULL) err(1, "fopen"); free(tangledfilename); 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; } finish2: line[line_l] = '\0'; line_l = 0; /* reset line length count */ ref = line; for (indent = 0; *ref == ' '; ref++) indent++; if (strncmp(ref, "<<", 2) != 0 || strncmp(ref + strlen(ref) - 2, ">>", 2) != 0) { fprintf(f, "%s\n", line); continue; } ref += 2; ref[strlen(ref) - 2] = '\0'; for (i = 0; i < strlen(ref); i++) if (isspace(ref[i])) { fprintf(f, "%s\n", line); continue; } if (strlen(ref) > REFMAX) fprintf(stderr, "Warning: Truncating identifier exceeding %d characters\n", REFMAX); 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: if (ins[i] == NULL) { fprintf(stderr, "Warning: Insertion for %s is empty\n", ref); continue; } 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]); } } if (c != '\n') { c = '\n'; goto finish2; } fclose(f); fclose(fo); } return 0; } void reference(char *line) { char *ln = line; int i; int j; char **tmp; char ***tmp2; if (strncmp(ln, doc_prefix, strlen(doc_prefix)) != 0) return; hyphen: if (*ln == '\0') return; else if (*ln == '-') { ln++; goto lessthan; } else { ln++; goto hyphen; } lessthan: if (*ln != '>') goto hyphen; else ln++; space: if (isspace(*ln)) { ln++; goto space; } if (*ln == '\0') { ref = ""; return; } for (i = 0; i < strlen(ln); i++) if (isspace(ln[i])) { for (j = i; j < strlen(ln); j++) if (!isspace(ln[j])) return; break; } ln[i] = '\0'; if (strlen(ln) > REFMAX) { fprintf(stderr, "Warning: Truncating identifier exceeding %d characters\n", REFMAX); ln[REFMAX] = '\0'; } sprintf(ref, "%s", ln); /* set current reference */ ref[strlen(ln)] = '\0'; for (i = 0; i < refs_c; i++) if (strcmp(refs[i], ref) == 0) return; fprintf(stderr, "New reference: %s\n", ref); if (++refs_c > refs_s) { refs_s += 10; tmp = realloc(refs, refs_s * sizeof(char *)); if (tmp == NULL) err(1, "malloc"); refs = tmp; tmp2 = realloc(ins, refs_s * sizeof(char *)); if (tmp2 == NULL) err(1, "malloc"); ins = tmp2; for (i = refs_s - 10; i < refs_s; i++) /* TODO: is this right? */ ins[i] = NULL; } refs[refs_c-1] = malloc(1 + REFMAX * sizeof(char)); sprintf(refs[refs_c-1], "%s", ref); } bool insertion(char *line) { int i; char **tmp; int len; if (ref[0] == '\0') return false; if (strlen(code_prefix) > 0) if (strncmp(line, code_prefix, strlen(code_prefix)) != 0) return false; if (strlen(doc_prefix) > 0) if (strncmp(line, doc_prefix, strlen(doc_prefix)) == 0) return false; for (i = 0; i < refs_c; i++) if (strcmp(refs[i], ref) == 0) break; if (ins[i] == NULL) { ins[i] = malloc(1 + 1 * sizeof(char *)); if (ins[i] == NULL) err(1, "malloc"); len = 0; } else { for (len = 0; ins[i][len] != NULL; len++) ; tmp = realloc(ins[i], 1 + (len + 1) * sizeof(char *)); if (tmp == NULL) err(1, "malloc"); ins[i] = tmp; } ins[i][len + 1] = NULL; ins[i][len] = malloc(1 + strlen(line) * sizeof(char)); if (ins[i][len] == NULL) err(1, "malloc"); strncpy(ins[i][len], line + strlen(code_prefix), strlen(line) - strlen(code_prefix)); ins[i][len][strlen(line) - strlen(code_prefix)] = '\0'; return true; }