|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - download
Length: 9487 (0x250f) Types: TextFile Notes: UNIX file Names: »deroff.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code └─⟦f4b8d8c84⟧ UNIX Filesystem └─ ⟦this⟧ »cmd/deroff.c«
/* * Strip nroff/troff control lines and eqn * and tbl sequences from input. * Also, optionally, produce the * output as a set of words. */ #include <stdio.h> #include <ctype.h> #define NLINE 500 /* Input line length */ #define NFNEST 15 /* Depth of .so file nesting */ FILE *ofiles[NFNEST]; FILE **ofpp = &ofiles[0]; char **flist; /* list of files to open */ typedef struct FNAME { struct FNAME *fn_next; char fn_name[]; } FNAME; FNAME *fnames; char line[NLINE]; int delim1 = EOF; /* Start embedded eqn */ int delim2 = EOF; /* End embedd eqn */ int skipcnt; /* Number of lines to skip */ int skiptitle; /* Skip title text (until next nroff command) */ int sflag; /* Divide into sentences */ int wflag; /* Divide output into words */ int xflag; /* Extra knowledge of macros (style/diction) */ int ineqn; /* Inside embedded eqn escape */ FILE *dopen(); char *dgets(); main(argc, argv) int argc; char *argv[]; { register char *ap; while (argc>1 && *argv[1]=='-') { for (ap = &argv[1][1]; *ap!='\0'; ap++) switch (*ap) { case 's': sflag = 1; break; case 'w': wflag = 1; break; case 'x': xflag = 1; break; default: usage(); } argv++; argc--; } if (argc < 2) ofiles[0] = stdin; flist = &argv[1]; exit (deroff()); } /* * Read until end-of-file * and process the special nroff/troff/eqn/tbl * lines in the file. */ deroff() { while (dgets(line) != NULL) { if (!ineqn && line[0]=='.') { nroff(line); continue; } output(line); } } /* * O▶15◀utput for that line which isn't an nroff * control line. This has to look for embedded * eqn stuff and back-slash troff/nroff escapes. * The embedded escapes are all handled to some degree * but such things as nested quotes (e.g. \w or \h) * do not quite work. However, these occur almost never * in text so it should be sufficient. */ output(l) register char *l; { register int c; register int inword = 0; register int hyphen; if (skipcnt) { skipcnt--; return; } if (skiptitle) return; while ((c = *l++) != '\0') { if (ineqn) { if (c == delim2) ineqn = 0; continue; } if (c == delim1) { ineqn = 1; continue; } if (c == '\\') { if ((c = *l++) == '\0') break; switch (c) { case '0': /* digit width space */ case '|': /* Narrow space */ case '^': /* Half narrow space */ case '&': /* Non-printing, 0-width char */ case '!': /* Transparent line indicator */ case 'e': /* Current escape */ case '%': /* Optional hyphenation char */ case 't': /* Non-interpreted tab */ case 'u': /* Up 1/2 */ case 'd': /* Down 1/2 */ case 'a': /* Non-interpeted leader */ case 'c': /* Interrupt text processing */ case 'p': /* Break and spread */ case 'r': /* Rerverse vertical motion */ case '{': /* Begin conditional */ case '}': /* End conditional */ c = ' '; break; case '$': /* argument */ if (*l != '\0') l++; continue; case '(': /* Char named `xx' */ if (*l != '\0') l++; if (*l != '\0') l++; c = ' '; break; case 'z': /* Zero-width character */ if (*l != '\0') c = *l++; break; case 'k': /* Mark input place in `x' */ case 'n': /* Expand reggister x */ case '*': /* Interpolate string */ case 'f': /* Change font */ if (*l != '\0') if ((c = *l++) == '(') { if (*l != '\0') l++; if (*l != '\0') l++; } continue; case 's': /* Change point size */ if (*l == '\0') continue; if ((c = *l++)=='-' || c=='+') { if (*l == '\0') continue; c = *l++; } while (*l!='\0' && isdigit(c)) c = *l++; break; case 'x': /* Extra line space */ case 'w': /* Width function */ case 'v': /* Local vertical motion */ case 'o': /* Overstrike function */ case 'L': /* Vertical line */ case 'l': /* Horizontal line */ case 'h': /* Local horizontal motion */ case 'b': /* Bracket-builder */ if ((c = *l) != '\0') while (*l!='\0' && *l!=c) l++; continue; case '"': /* Beginning of comment */ while (*l != '\0') l++; continue; } } if (wflag) { if (c == '\n') continue; if (!inword) if (isalpha(c)) inword = 1; else continue; if (c=='-' && !hyphen) { hyphen = 1; continue; } hyphen = 0; if (c == '\'') continue; if (!isalpha(c) && !isdigit(c)) { inword = 0; putchar('\n'); continue; } } putchar(c); } if (wflag && inword && !hyphen) putchar('\n'); } /* * Process nroff control lines. * Remove EQN, TBL, macro defintions. * Process .so and .nx here. * Other lines have the rest of the line used. */ nroff(l) register char *l; { skiptitle = 0; if (l[1]=='E' && l[2]=='Q') eqn(); else if (l[1]=='T' && l[2]=='S') tbl(); else if (l[1]=='F' && l[2]=='S') footnote(); else if (l[1]=='c' && l[2]=='e') centre(l); else if (l[1]=='n' && l[2]=='f') nofill(); else if (l[1]=='D' && l[2]=='S') display(); else if (l[1]=='K' && (l[2]=='F' || l[2]=='S')) display(); else if (l[1]=='T' && l[2]=='L') titles(); else if (l[1]=='A' && (l[2]=='I' || l[2]=='U')) titles(); else if (l[2]=='H' && (l[1]=='S' || l[1]=='N')) titles(); else if (l[1]=='n' && l[2]=='x') include(line+3, 'n'); else if (l[1]=='s' && l[2]=='o') include(line+3, 's'); else if (l[1]=='d' && l[2]=='e') macdef(); else if (l[1]=='d' && l[2]=='s') return; else { while (*l!=' ' && *l!='\t' && *l!='\0') l++; while (*l==' ' || *l=='\t') l++; if (*l != '\0') output(l); } } /* * Process included files. * The first argument is the pointer to where * the filename is (it may have junk before and after it) * The second is 's' for .so and 'n' for .nx. */ include(fn, type) register char *fn; char type; { register int c; register char *ep; while (*fn==' ' || *fn=='\t') fn++; for (ep = fn; (c = *ep)!='\0'; ep++) if (c=='\n' || c==' ' || c=='\t' || c=='\\') break; *ep = '\0'; if (type == 's') dotso(fn); else *ofpp = dopen(fn); } /* * Process eqn directives. * Currently, this simply looks for * .EN lines as the terminator * and delim lines to set the eqn delimiters. */ eqn() { register char *cp; while (dgets(line) != NULL) { if (strncmp(line, ".EN", 3) == 0) break; if (strncmp(line, "delim", 5) == 0) { for (cp = line+5; *cp==' ' || *cp=='\t'; cp++) ; if (*cp=='\n' || *cp=='\0') continue; if (strncmp(cp, "off", 3) == 0) { delim1 = EOF; delim2 = EOF; continue; } delim1 = *cp++; delim2 = *cp; } } } /* * Process tbl directives. At this time, * all this does is look for the terminating * .TE to end tables. */ tbl() { while (dgets(line) != NULL) if (strncmp(line, ".TE", 3) == 0) break; } /* * In extended knowledge mode (-ms macros), * remove footnotes. This mode is for * style and diction. */ footnote() { if (!xflag) return; while (dgets(line) != NULL) if (strncmp(line, ".FE", 3) == 0) break; } /* * Throw away nofilled text as with footnotes above. */ nofill() { if (!xflag) return; while (dgets(line) != NULL) if (strncmp(line, ".fi", 3) == 0) break; } /* * Skip centred lines, in extended mode, * by setting a skip counter on text. */ centre(l) char *l; { if ((skipcnt = atoi(l)) == 0) skipcnt = 1; } /* * Skip displays in extended mode. */ display() { if (!xflag) return; while (dgets(line) != NULL) { if (strncmp(line, ".KE", 3) == 0) break; if (strncmp(line, ".DE", 3) == 0) break; } } /* * If in extended mode, skip titles and author's * names. Set a flag to skip until next nroff command. */ titles() { if (xflag) skiptitle = 1; } /* * Remove a macro defintion. */ macdef() { while (dgets(line) != NULL) if (strcmp(line, "..\n") == 0) break; } /* * Get a character from the next file stream. */ dgetc() { register int c; again: if (*ofpp==NULL || (c = getc(*ofpp))==EOF) { if (*ofpp!=stdin && *ofpp!=NULL) fclose(*ofpp); if (ofpp > ofiles) { ofpp--; goto again; } while (*flist != NULL) if ((*ofpp = dopen(*flist++)) != NULL) goto again; return (EOF); } return (c); } /* * Like fgets, only always reads using `dgetc' * into a buffer of `NLINE' characters. */ char * dgets(as) char *as; { register unsigned n = NLINE; register char *s; register int c; s = as; while (--n>0 && (c = dgetc())!=EOF) if ((*s++ = c) == '\n') break; *s = '\0'; return (c==EOF && s==as ? NULL : as); } /* * Open input files (for .so, .nx, and from * command line). Do not open any files twice. */ FILE * dopen(fname) register char *fname; { register FNAME *fnp; register FILE *fp; for (fnp = fnames; fnp != NULL; fnp = fnp->fn_next) if (strcmp(fnp->fn_name, fname) == 0) return (NULL); if ((fp = fopen(fname, "r")) == NULL) fprintf(stderr, "deroff: cannot open `%s'\n", fname); else if ((fnp=(FNAME *)malloc(sizeof(FNAME)+strlen(fname)+2)) != NULL) { fnp->fn_next = fnames; fnames = fnp; strcpy(fnp->fn_name, fname); } return (fp); } /* * Include a file as per the `.so' request * line. */ dotso(fname) char *fname; { if (++ofpp >= &ofiles[NFNEST]) { fprintf(stderr, "deroff: .so nested too deep--%s\n", fname); ofpp--; return; } if ((*ofpp = dopen(fname)) == NULL) ofpp--; } usage() { fprintf(stderr, "Usage: deroff [ -w ] [ -x ] [file ...]\n"); exit(1); }