jejones@ea.UUCP (12/21/84)
Yet another possibly useful tool: this one splits out fields from input lines and writes them out in columnar fashion. Admittedly this one, like the sort that preceded it, isn't particularly OS-9 or 6809-specific, but then there are places that lock up net.sources, and I want the OS-9 users out there to get these tools. James Jones ------------------------------TIARA HERE------------------------------ /* * column -- print with fields justified and columnized * * usage: column [<options>] [<field-spec> ... ] [<pathname> ...] * where <field-spec> has the form * <field #>/<max width>[<justify>] * and <justify> can be either 'l' or 'r'. * (Upper or lower case will do.) * * options: -c=<character> use the <character> as a field separator * -l<length> assume the output lines have maximum length * <length> * * semantics: reads files with the specified pathnames and prints the * fields selected from each line so that they fit on a line * at most <length> characters long, with each field lined up * in columnar fashion. Fields longer than their <max width> * are truncated. Spaces left over are divided evenly * between columns. * * defaults: field separator defaults to space, <length> to 80. * If no fields are specified, then 1/<length>l is assumed. * Justification defaults to left-justification. * If no pathnames are specified, standard input is read. * * limits: Lines are truncated at MAXLINE characters. * We can output at most MAXFIELDS fields, and read * at most MAXFILES files. * We are currently stupid about tabs and backspaces * when we figure out what fits on a line. */ #include <stdio.h> #include <ctype.h> #include <bool.h> #define MAXLINE 256 #define MAXFILES 20 #define MAXFIELDS 10 #define error(msg) {fprintf(stderr, "column: %s\n", (msg)); exit(1);} #define Space(size) {int i; for (i = 0; i < (size); i++) putchar(' ');} #define DELIM 'c' #define LENGTH 'l' #define LJUST 'L' #define RJUST 'R' typedef struct { int f_no; int f_maxlen; char f_justify; int f_gutter; char *f_pos; int f_trulen; } FldDesc; FldDesc LineFields[MAXFIELDS], *FldSeq[MAXFIELDS]; int NFields; char *Input[MAXFILES]; int NInputs, CurrPath; FILE *CurrSource; char Delim = ' '; int Length = 80; main(argc, argv) int argc; char *argv[]; { int i; char Line[MAXLINE]; char *InLine(); bool OK, DoOpt(), DoFile(), InitLayout(), StartRead(); OK = TRUE; for (i = 1; i < argc; i++) { if (*argv[i] == '-') OK &= DoOpt(argv[i]); else if (isdigit(*argv[i])) OK &= DoFld(argv[i]); else OK &= DoFile(argv[i]); } if (!OK || !InitLayout() || !StartRead()) exit(1); while (InLine(Line) != NULL) Columnize(Line); } bool DoOpt(OpString) char *OpString; { switch (*(OpString + 1)) { case DELIM: if (*(OpString + 2) != '=') error("invalid delimiter specification"); Delim = *(OpString + 3); break; case LENGTH: if ((Length = atoi(OpString + 2)) <= 0) error("invalid line length"); break; default: fprintf(stderr, "column: invalid option %c\n", *(OpString + 1)); return(FALSE); } return(TRUE); } bool DoFld(FldString) char *FldString; { FldDesc *NewFld; FldSeq[NFields] = NewFld = &LineFields[NFields]; if ((NewFld->f_no = atoi(FldString)) <= 0) error("invalid field number"); while (isdigit(*(FldString++))) ; if (*(FldString - 1) != '/') error("invalid field specification"); if ((NewFld->f_maxlen = atoi(FldString)) <= 0) error("invalid field width"); while (isdigit(*(FldString++))) ; switch (toupper(*(FldString - 1))) { case RJUST: NewFld->f_justify = RJUST; break; case '\0': case LJUST: NewFld->f_justify = LJUST; break; default: error("invalid justification"); } NFields++; return(TRUE); } /* * routines to hide the multiplicity of input files from the main * algorithm -- DoFile, StartRead, and InLine. */ bool DoFile(PathName) char *PathName; { if (NInputs >= MAXFILES) error("too many input files"); Input[NInputs++] = PathName; return(TRUE); } bool StartRead() { if (NInputs > 0) { if ((CurrSource = fopen(Input[0], "r")) == NULL) { fprintf(stderr, "sort: can't open %s\n", Input[0]); return(FALSE); } } else CurrSource = stdin; return(TRUE); } char * InLine(Buffer) char *Buffer; { char *result; while ((result = fgets(Buffer, MAXLINE, CurrSource)) == NULL) { fclose(CurrSource); if (++CurrPath >= NInputs) return(NULL); if ((CurrSource = fopen(Input[CurrPath], "r")) == NULL) { fprintf(stderr, "sort: can't open %s\n", Input[CurrPath]); exit(1); } } return(result); } bool InitLayout() { int i, totlen, gap, leftover; int Precede(); if (NFields == 0) { FldSeq[0] = &LineFields[0]; LineFields[0].f_no = 1; LineFields[0].f_maxlen = Length; LineFields[0].f_justify = LJUST; } else if (NFields > 1) { totlen = 0; for (i = 0; i < NFields; i++) totlen += LineFields[i].f_maxlen; if ((gap = (Length - totlen) / (NFields - 1)) < 1) error("fields too big for output line"); leftover = (Length - totlen) % (NFields - 1); for (i = 0; i < NFields - 1; i++) LineFields[i].f_gutter = gap + (i < leftover); qsort(FldSeq, NFields, sizeof(FldDesc *), Precede); } LineFields[NFields - 1].f_gutter = 0; return(TRUE); } int Precede(f1, f2) FldDesc **f1, **f2; { return ((*f1)->f_no - (*f2)->f_no); } Columnize(Line) char *Line; { char *Field; int i, CurrField, FScan, FLen; bool ShouldOutput; Field = Line; Field[strlen(Field) - 1] = Delim; for (CurrField = 1, FScan = 0; FScan < NFields; CurrField++) { ShouldOutput = CurrField >= FldSeq[FScan]->f_no; if (ShouldOutput) FldSeq[FScan]->f_pos = Field; FLen = 0; if (*Field != '\0') { while (*(Field++) != Delim) FLen++; *(Field - 1) = '\0'; } if (ShouldOutput) { FldSeq[FScan]->f_trulen = FLen; FScan++; } } for (FScan = 0; FScan < NFields; FScan++) PrintField(&LineFields[FScan]); putchar('\n'); } PrintField(RefFld) FldDesc *RefFld; { if (RefFld->f_trulen > RefFld->f_maxlen) { (RefFld->f_pos)[RefFld->f_maxlen] = '\0'; RefFld->f_trulen = RefFld->f_maxlen; } switch (RefFld->f_justify) { case RJUST: Space(RefFld->f_maxlen - RefFld->f_trulen); fputs(RefFld->f_pos, stdout); break; case LJUST: fputs(RefFld->f_pos, stdout); Space(RefFld->f_maxlen - RefFld->f_trulen); break; } Space(RefFld->f_gutter); }