/* tt.c -- tangle to, written by John Ankarström */ #include #ifdef _WIN32 #include #pragma comment(lib, "Shlwapi.lib") #else #include #include #include #include #include #include #endif #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 #define REFMAX 80 char *code_prefix; char *doc_prefix; char *ref; /* current reference */ char ***ins; /* insertions */ char **refs; /* references */ int refs_c; /* count */ int refs_s; /* size */ void reference(char *line) { int i, j; char *ln, **tmp, ***tmp2; ln = line; /* parse: -> identifier_without_whitespace */ start: if (*ln == '\0') return; else if (*ln == '-') { ln++; goto arrow; } else { ln++; goto start; } arrow: if (*ln != '>') goto start; else ln++; space: if (*ln == ' ') { ln++; goto space; } 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'; } ref = malloc(1 * strlen(ln) * sizeof(ln)); 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 code(char *line) { char **tmp; int i, j, 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; } int main(int argc, char *argv[]) { bool finish, iscode, wascode; char **a, b, c, *line, *out_prefix, *tangledfile, *tmp; FILE *f, *fo; int i, j, k, m; int indent, line_l, line_s, offset; ref = malloc(1 + (REFMAX + 4) * sizeof(char)); /* incl. << and >> */ if (ref == NULL) err(1, "malloc"); refs_c = 0; refs_s = 10; refs = malloc(refs_s * sizeof(char *)); ins = malloc(refs_s * sizeof(char **)); if (refs == NULL || ins == NULL) err(1, "malloc"); for (i = 0; i < refs_s; i++) ins[i] = NULL; code_prefix = " "; doc_prefix = ""; out_prefix = "out/"; 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; } } else break; 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"); offset = i; if (offset == argc) die("usage: %s destination ...\n", argv[0]); line_l = 0; line_s = 100; line = malloc(1 + line_s * sizeof(char)); if (line == NULL) err(1, "malloc"); finish = false; iscode = false; wascode = false; while ((b = getchar()) != 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; } finish: line[line_l] = '\0'; line_l = 0; if (strlen(code_prefix) == 0 && !wascode && strcmp(line, "") == 0) { continue; /* ignore empty lines after doc text */ } iscode = code(line); if (!iscode) reference(line); if (strlen(code_prefix) > 0 && wascode && !iscode) { code(code_prefix); /* add extra empty line after code block */ } wascode = iscode; } if (c != '\n' && !finish) { finish = true; goto finish; } free(ref); for (k = offset; k < argc; k++) { tangledfile = malloc( 1 + (strlen(out_prefix) + 50 + sizeof(argv[k])) * sizeof(char) /* ??? */ ); if (tangledfile == NULL) err(1, "malloc"); if (sprintf(tangledfile, "%s%s", out_prefix, argv[k]) == -1) err(1, "sprintf"); f = fopen(tangledfile, "w"); if (f == NULL) err(1, "fopen"); fo = fopen(argv[k], "r"); if (fo == NULL) err(1, "fopen"); free(tangledfile); line_l = 0; finish = false; 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; } 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' && !finish) { finish = true; goto finish2; } fclose(f); fclose(fo); } return 0; }