Path: seismo!lll-crg!caip!topaz!harvard!husc6!panda!sources-request From: sources-request@panda.UUCP Newsgroups: mod.sources Subject: Fmtr - simple text formatter Message-ID: <1872@panda.UUCP> Date: 15 May 86 20:21:48 GMT Sender: jpn@panda.UUCP Lines: 1377 Approved: jpn@panda.UUCP Mod.sources: Volume 4, Issue 120 Submitted by: harvard!hscfvax!popvax!mohamed (Mohamed ellozy) Fmtr is a simple text formatter which evens out the lines of files prepared for submission to the Unix formatters nroff, troff, or ditroff. It is for users who find editing neatly formatted files pleasanter than working with files with irregular lines. It knows that lines starting with a period or with an apostrophe should be output as is, and that certain requests and macros start no-fill mode, while others return to fill mode. So, unlike fmt, it will process the following example properly: .TS n n. 1 2 3 4 .TE Fmt run on that example would produce: .TS n n. 1 2 3 4 .TE destroying the table. Mohamed el Lozy mohamed@hscfvax.uucp Health Sciences Computing Facility ...!harvard!hscfvax!mohamed Harvard School of Public Health 665 Huntington Avenue Boston, MA 02115 ------------------------------ CUT HERE ------------------------------ #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # README # Makefile # command.c # efopen.3s # efopen.c # fgetsmod.3s # fgetsmod.c # fmtr.1 # fmtr.c # fmtr.h # getopt.3 # getopt.c # lowtext.c # This archive created: Thu May 15 16:20:30 1986 export PATH; PATH=/bin:$PATH echo shar: extracting "'README'" '(1089 characters)' if test -f 'README' then echo shar: will not over-write existing file "'README'" else cat << \SHAR_EOF > 'README' The shell archive contains the files needed to make fmtr, a formatter for {n,t,dit}roff input files. Four of the source files: fmtr.h fmtr.c command.c lowtext.c contain the program proper. In addition it uses three functions that I have put in my library: getopt() a public domain version of the System V utility apparently due to Henry Spencer taht I have modified minimally efopen() a modified version of K & P's function fgetsmod() a modified version of fgets() that truncates lines longer than the array they are being read into. Manual pages for fmtr and for the three functions named above are included. The Makefile does not have any provision for adding these functions to any library. I have added them to /lib/libc.a, you may want to put them elsewhere. I have also not put in any provision for installing the manual pages associated with them. Comments, bug reports etc to: Mohamed el Lozy mohamed@hscfvax.uucp Health Sciences Computing Facility ...!harvard!hscfvax!mohamed Harvard School of Public Health 665 Huntington Avenue Boston, MA 02115 SHAR_EOF if test 1089 -ne "`wc -c < 'README'`" then echo shar: error transmitting "'README'" '(should have been 1089 characters)' fi chmod +x 'README' fi echo shar: extracting "'Makefile'" '(789 characters)' if test -f 'Makefile' then echo shar: will not over-write existing file "'Makefile'" else cat << \SHAR_EOF > 'Makefile' CFLAGS = -O # comment out next line if you have getopt() in your library GETOPT = getopt.o # comment out next line if you have efopen() in your library EFOPEN = efopen.o # comment out next line if you have fgetsmod() in your library FGETSMOD = fgetsmod.o SRC = fmtr.c command.c lowtext.c efopen.c getopt.c fgetsmod.c OBJ = fmtr.o command.o lowtext.o $(EFOPEN) $(GETOPT) $(FGETSMOD) # You may wish to modify either or both of the following: BINDIR = /usr/local MANDIR = /usr/man/man1 fmtr: $(OBJ) cc $(CFLAGS) -s -o fmtr $(OBJ) fmtr.o command.o lowtext.o: fmtr.h install: fmtr fmtr.1h cp -c fmtr $(BINDIR) cp -c fmtr.1h $(MANDIR) shar: shar -a README Makefile $(SRC) fmtr.h fmtr.1 efopen.3s \ fgetsmod.3s getopt.3 > fmtr.shar clean: /bin/rm -f *.o a.out core fmtr fmtr.shar SHAR_EOF if test 789 -ne "`wc -c < 'Makefile'`" then echo shar: error transmitting "'Makefile'" '(should have been 789 characters)' fi chmod +x 'Makefile' fi echo shar: extracting "'command.c'" '(4522 characters)' if test -f 'command.c' then echo shar: will not over-write existing file "'command.c'" else cat << \SHAR_EOF > 'command.c' #ifndef lint static char rcsid[] = "$Header: command.c,v 1.4 86/05/05 14:08:32 root Exp $"; #endif /* command.c decodes command. Uses binary search in array * of struct cmd. */ #include "fmtr.h" #define HUGE 1000 #define PLUS '+' #define MINUS '-' #define MAX(x,y) ((x) > (y) ? (x) : (y)) #define MIN(x,y) ((x) < (y) ? (x) : (y)) /* cmdtype is upper case version of command. All commands of * interest to us are either ce, ul, or else equivalent to * fi or nf */ enum cmdtype { CE, UL, FI, NF, OTHER } cmd, getcmd(); int len; struct cmd { char *name; enum cmdtype type; } cmd_table[50] = { "ce", CE, /* basic nroff requests */ "ul", UL, "nf", NF, "fi", FI, "TS", NF, /* universal macros */ "TE", FI, "EQ", NF, "EN", FI, "PS", NF, "PE", FI, "IS", NF, "IE", FI, "DS", NF, /* ms macros */ "ID", NF, "CD", NF, "LD", NF, "DE", FI, "(b", NF, /* me macros */ ")b", FI, "(c", NF, ")c", FI, "(l", NF, ")l", FI, "(z", NF, ")z", FI, (char *) NULL, OTHER }; int val; /* both are set in getval() */ char argtype; /* and used in setparam() */ /* command() takes a line starting with some form of the command start * sequence (period or apostrophe, optionally preceded by \&), and * calls getcmd() to return an integer representing the command name, * and then takes appropriate action. * * In all cases it produces a break and prints out the command line as is. */ command(line) char line[]; { cmd = getcmd(line); if (cmd != OTHER) switch (cmd) { case CE: getval(line); /* only need getval() with CE and UL */ ce_val = setparam(ce_val, 1, 0, HUGE); break; case UL: getval(line); ul_val = setparam(ul_val, 1, 0, HUGE); break; case NF: nf_val = 1; break; case FI: nf_val = 0; break; } n_brk(); puts(line); } enum cmdtype getcmd(line) /* gets command type by binary search */ char *line; /* stolen from K & R p. 125 with minor changes */ { int high, low, mid, cond; char *cp; low = 0; high = len - 1; cp = (*line == '\\') ? line + 3 : line + 1; while (low <= high) { mid = (low + high)/2; if ((cond = strncmp(cp, cmd_table[mid].name, 2)) < 0) high = mid - 1; else if (cond > 0) low = mid + 1; else return(cmd_table[mid].type); } return(OTHER); } mk_table(sarray, earray) char *sarray, *earray; { int mycmp(); char *cp, *malloc(), *strncpy(); struct cmd *cmdptr; if (len == 0) { /* find end */ cmdptr = cmd_table; while (cmdptr->name) cmdptr++; /* now pointing to NULL ending defined */ len = cmdptr - cmd_table; } else cmdptr = cmd_table + len; if (sarray) for (cp = sarray; *cp; cp += 3) { cmdptr->name = malloc(3); (void) strncpy(cmdptr->name, cp + 1, 2); cmdptr->type = NF; cmdptr++; } if (earray) for (cp = earray; *cp; cp += 3) { cmdptr->name = malloc(3); (void) strncpy(cmdptr->name, cp + 1, 2); cmdptr->type = FI; cmdptr++; } len = cmdptr - cmd_table; qsort((char *) cmd_table, len, sizeof cmd_table[0], mycmp); } mycmp(s1, s2) struct cmd *s1, *s2; { return(strcmp(s1->name, s2->name)); } /* getval() gets value of argument preceded by optional sign. Here we are following the nroff rules: commands are exactly two letters long followed by optional spaces before arguments. */ getval(line) char *line; { char *cp; if (*line == '\\') /* don't test for z_flag, since otherwise */ cp = line + 5; /* we would not be here */ else cp = line + 3; for ( ; isspace(*cp); *cp++) ; argtype = *cp; if (argtype == PLUS || argtype == MINUS) cp++; val = atoi(cp); } /* setparam() sets parameter. May be set to param if present and legal, * otherwise to def_val, if absent, less than min_val or greater than * max_val. */ setparam(param, def_val, min_val, max_val) int param, def_val, min_val, max_val; { if (argtype == '\0') return(def_val); else if (argtype == PLUS) param += val; else if (argtype == MINUS) param -= val; else param = val; param = MAX(param, min_val); param = MIN(param, max_val); return(param); } #ifdef DEBUG print_tab() /* prints table, useful while debugging */ { struct cmd *cmdptr; for (cmdptr = cmd_table; cmdptr < cmd_table + len; cmdptr++) printf("%s\n", cmdptr->name); } #endif SHAR_EOF if test 4522 -ne "`wc -c < 'command.c'`" then echo shar: error transmitting "'command.c'" '(should have been 4522 characters)' fi chmod +x 'command.c' fi echo shar: extracting "'efopen.3s'" '(918 characters)' if test -f 'efopen.3s' then echo shar: will not over-write existing file "'efopen.3s'" else cat << \SHAR_EOF > 'efopen.3s' .TH EFOPEN 3S "11 May 1986" .SH NAME efopen \- open a stream, exiting with message in case of failure .SH SYNOPSIS .B #include .br .B extern char *progname; .PP .SM .B FILE .B *efopen(filename, type) .br .B char *filename, *type; .SH DESCRIPTION .I Efopen calls .IR fopen (3S) to open the file named by .IR filename , and if successful returns a pointer to be used to identify the stream in subsequent operations. .PP If .I filename is the string \- .I efopen will return .IR stdin . .PP .I Type is a character string as in .IR fopen (3S). .PP On failure, .I efopen exits, after printing out the name of the program (if a value has been given to .B progname by main), the name of the file, and calling .IR perror (3). .SH "SEE ALSO" fopen(3S), open(2), fclose(3) .SH "AUTHOR" Modified by Mohamed el Lozy from program given in .I "The UNIX Programming Environment" by Brian W. Kernighan and Rob Pike, p 182. SHAR_EOF if test 918 -ne "`wc -c < 'efopen.3s'`" then echo shar: error transmitting "'efopen.3s'" '(should have been 918 characters)' fi chmod +x 'efopen.3s' fi echo shar: extracting "'efopen.c'" '(591 characters)' if test -f 'efopen.c' then echo shar: will not over-write existing file "'efopen.c'" else cat << \SHAR_EOF > 'efopen.c' #ifndef lint static char rcsid[] = "$Header: efopen.c,v 1.3 86/05/11 09:50:48 root Exp $"; #endif #include FILE * efopen(file, mode) /* fopen file, die if cannot */ char *file, *mode; /* from K & P with addition of perror() and handling of "-" as stdin */ { FILE *fp; extern char *progname; if (strcmp(file, "-") == 0) return(stdin); if ((fp = fopen(file, mode)) != NULL) return (fp); if (progname) fprintf(stderr, "%s ", progname); fprintf(stderr, "can't open file %s mode %s: ", file, mode); perror(""); exit(1); /* NOTREACHED */ } SHAR_EOF if test 591 -ne "`wc -c < 'efopen.c'`" then echo shar: error transmitting "'efopen.c'" '(should have been 591 characters)' fi chmod +x 'efopen.c' fi echo shar: extracting "'fgetsmod.3s'" '(1470 characters)' if test -f 'fgetsmod.3s' then echo shar: will not over-write existing file "'fgetsmod.3s'" else cat << \SHAR_EOF > 'fgetsmod.3s' .TH FGETSMOD 3S "11 May 1986" .SH NAME fgetsmod \- get a line from a stream, truncating it if it is too long .SH SYNOPSIS .B #include .br .B #define TOOLONG -2 .PP .B int *fgets(s, n, stream) .br .B char *s; .br .SM .B FILE .B *stream; .SH DESCRIPTION .I Fgetsmod is a modified version of .IR fgets , which truncates lines longer than the size of the array into which they are being read. It reads .IR n \-2 characters, or up to a newline character, whichever comes first, from the .I stream into the string .IR s . If no newline is found after reading the first .IR n \-2 characters, .I fgetsmod will add a newline and null character, then read and discard characters from the input stream up to a newline. In other words, it will discard characters in excess of the capacity of .IR s . The last character read into .I s is followed by a null character. .I Fgetsmod returns the number of characters read if it can read the whole line. .SH "SEE ALSO" gets(3S), puts(3S), getc(3S), scanf(3S), fread(3S), ferror(3S) .SH AUTHOR Mohamed el Lozy, Harvard Health Sciences Computing Faility .SH DIAGNOSTICS .I Fgetsmod return the integer zero upon end of file or read error. It returns \-2 (to avoid confusion with .BR \s9EOF\s0 ) if the line is too long. .SH BUGS .I Fgetsmod returns an .IR int , unlike .IR fgets , which returns a .IR char . It should therefore probably return .SM .B EOF on end of file, but returning zero makes it more similar to .IR fgets . SHAR_EOF if test 1470 -ne "`wc -c < 'fgetsmod.3s'`" then echo shar: error transmitting "'fgetsmod.3s'" '(should have been 1470 characters)' fi chmod +x 'fgetsmod.3s' fi echo shar: extracting "'fgetsmod.c'" '(1683 characters)' if test -f 'fgetsmod.c' then echo shar: will not over-write existing file "'fgetsmod.c'" else cat << \SHAR_EOF > 'fgetsmod.c' #ifndef lint static char rcsid[] = "$Header: fgetsmod.c,v 1.3 86/05/05 14:19:17 root Exp $"; #endif #include #define TOOLONG -2 /* New and improved version of fgets. Unlike original, eats up extra chars. * fgets1 will read n - 1 characters, or up to a new line, whichever * comes first, from stream iop into string s. The last character read * into s is followed by a null character. * * It deals with all possibilities. If line ends with newline or have * isolated EOF, no problem. Otherwise, it will insert a newline and eat * any excess characters. Hence guarantees line ending with newline * followed by null. * * It returns: * 1. NULL at end of file, for compatible with fgets. * 2. TOOLONG if line is too long. * This is usable as a warning. * 3. Length of line, excluding null (like strlen), otherwise. * This is useful in the usual case when line is read uneventfully. */ fgetsmod(s, n, iop) char *s; register FILE *iop; { register c; register char *cs; cs = s; while (--n > 0 && (c = getc(iop)) != EOF) { *cs++ = c; if (c == '\n') break; } if (c == '\n') { /* normal ending, commonest case */ *cs = '\0'; return (cs - s); } if ((c == EOF) && (cs == s)) /* isolated EOF, second commonest case */ return (NULL); if (n == 0) { /* line too long */ *cs = '\0'; *(--cs) = '\n'; /* put in missing newline */ while ((c = getc(iop)) != EOF && c != '\n') /* eat up extra chars */ ; return (TOOLONG); } if (c == EOF) { /* final line has no newline -- rare */ *cs++ = '\n'; *cs = '\0'; return (cs - s); /* pretend all was OK */ } /* NOTREACHED */ } SHAR_EOF if test 1683 -ne "`wc -c < 'fgetsmod.c'`" then echo shar: error transmitting "'fgetsmod.c'" '(should have been 1683 characters)' fi chmod +x 'fgetsmod.c' fi echo shar: extracting "'fmtr.1'" '(3397 characters)' if test -f 'fmtr.1' then echo shar: will not over-write existing file "'fmtr.1'" else cat << \SHAR_EOF > 'fmtr.1' .TH FMTR 1H LOCAL "4th Berkeley Distribution" .SH NAME fmtr \- simple formatter for .I roff source files .SH SYNOPSIS .B fmtr [ \-w .I width ] [ \-z ] [ \-s .I .s1.s2.s3 ... .sn ] [ \-e .I .e1.e2.e3 ... .en ] [ name ... ] .SH DESCRIPTION .I Fmtr is a simple text formatter which evens out the lines of files prepared for submission to the Unix formatters .IR nroff , .IR troff , or .IR ditroff . .PP It is for users who find editing neatly formatted files pleasanter than working with files with irregular lines. It knows that lines starting with a period or with an apostrophe should be output as is, and that certain requests and macros start no-fill mode, while others return to fill mode. So, unlike .IR fmt , it will process the following example properly: .sp .in +5 .nf Some short lines to be joined, then a table which should be respected: \&.TS n n. 1 2 3 4 \&.TE .nf More short lines to be joined. .in .sp .fi The above will produce: .sp .nf .in +5 Some short lines to be joined, then a table which should be respected: \&.TS n n. 1 2 3 4 \&.TE More short lines to be joined. .fi .sp .in .I Fmt run on that example would produce: .sp .in +5 .nf Some short lines to be joined, then a table which should be respected: \&.TS n n. 1 2 3 4 \&.TE More short lines to be joined. .fi .in .sp destroying the table. .PP .I Fmtr reads the concatenation of input files (or standard input if none are given) and produces on standard output a version of its input with lines as close as possible to 72 characters. This default line length may be modified with the .IR \-w (for width) flag. If you specify the .I \-z flag it will peek under a zero width character at the start of a line seeking a command. .I Fmtr knows about the .IR ms (7) and .IR me (7) macro packages. .PP Since .I fmtr knows and respects most formatter constructs, it can be safely used on an entire document, as long as that document does not contain any strange sequences of commands. .PP There are two ways in which additional macros that start or end no-fill mode may be specified to .IR fmtr . The .I \-s and .I \-e flags can be used to introduce sequences of macros that start and end no-fill mode respectively. Any number of macros, each of which must consist of a period followed by exactly two letters, can be concatenated into a string. So if you are using two macros, .I .OS and .IR .FS , both of which start no-fill mode, and a corresponding pair, .I .OE and .IR .FE , which end no-fill mode, you would enter .sp .ti +5 .I "fmtr -s .OS.FS -e .OE.FE file" .sp For users who regularly use macros of there own, the environmental variables .I FMTR_S and .I FMTR_E are taken as strings of macros which start and end no-fill mode. They should be in the same format as the strings used as arguments to the .I \-s and .I \-e flags. .SH "SEE ALSO" nroff(1), environ(7), ms(7), me(7) .SH AUTHOR Mohamed el Lozy, Health Sciences Computing Facility, Harvard School of Public Health .SH BUGS The program does not attempt to duplicate .IR fmt , and will not deal with mail headers or with indented (preformatted) text. .br The current version does not support the .I mm macros, I hope to correct that soon. .br Macros recognized must be exactly two characters long. .br .I Fmtr is not a full .I roff recognizer. For example, changing the definition of the control or the escape character, will cause confusion. SHAR_EOF if test 3397 -ne "`wc -c < 'fmtr.1'`" then echo shar: error transmitting "'fmtr.1'" '(should have been 3397 characters)' fi chmod +x 'fmtr.1' fi echo shar: extracting "'fmtr.c'" '(3468 characters)' if test -f 'fmtr.c' then echo shar: will not over-write existing file "'fmtr.c'" else cat << \SHAR_EOF > 'fmtr.c' #ifndef lint static char rcsid[] = "$Header: fmtr.c,v 1.4 86/05/05 14:09:41 root Exp $"; #endif /* fmtr, a formatter for roff source text respecting no fill mode. * The code is derived from a translation to C of Kernighan and * Plaugher's format program. */ #include "fmtr.h" int optind, opterr; char *optarg, *progname; /* program name */ main(argc, argv) int argc; char *argv[]; { int i, c; char *sarray, *earray, *getenv(); FILE *efopen(), *fp; progname = argv[0]; outp = outbuf; sarray = earray = (char *) NULL; llength = 72; /* default */ sarray = getenv("FMTR_S"); check(sarray, "environmental variable FMTR_S"); earray = getenv("FMTR_E"); check(earray, "environmental variable FMTR_E"); mk_table(sarray, earray); /* make table of commands from env var */ /* we call mk_table whether the arrays */ /* null or not */ sarray = earray = (char *) 0; while ((c = getopt(argc, argv, "zw:s:e:")) != EOF) switch (c) { case 'z': z_flag = 1; break; case 'w': llength = atoi(optarg); if (llength <= 0 || llength >= BUFSIZ - 2) { fprintf(stderr,"%s: bad line length: %s\n", progname, optarg); exit (1); } break; case 's': sarray = optarg; check(sarray, "-s flag"); break; case 'e': earray = optarg; check(earray, "-e flag"); break; case '?': usage(); exit(1); } mk_table(sarray, earray); /* make table of commands from flags */ /* again, always call */ argc -= optind; argv += optind; fp = stdin; i = 0; do { if (argc > 0) fp = efopen(*argv, "r"); do_fmt(fp); /* does real work */ argv++; } while (++i < argc); n_brk(); } check(string, origin) /* check format of string of macros */ char *string, *origin; { if (string == NULL) return; if ((string[0] != '.') || strlen(string) % 3 != 0) { fprintf(stderr, "%s: list of macros '%s' supplied with %s is not in correct format\n", progname, string, origin); exit(1); } } /* do_fmt is where work is done, or rather assigned. Each line * read has the terminatig newline, and any trailing blanks, removed. * Then it is sent to either command() or text(), depending on whether * it appears to be a command or not. */ do_fmt(fp) FILE *fp; { char *cp, line[10*BUFSIZ]; int length; while (length = fgetsmod(line, sizeof line, fp)) { for (cp = &line[length - 2]; *cp == ' ' && cp >= line; cp--) ; *++cp = '\0'; if (*line == '.' || *line == '\'' || (z_flag && strncmp(line, "\\&.", 3) == 0) || (z_flag && strncmp(line, "\\&'", 3) == 0)) command(line); else text(line); } } /* text() checks for leading blanks or blank line, in which case causes * break. Blank lines output as is, others broken into words by getword() * with words sent to outbuf by putword(). */ text(line) char *line; { char *pline, wordbuf[BUFSIZ], *getword(); if (ul_val || ce_val || nf_val) { puts(line); if (ul_val) ul_val--; if (ce_val) ce_val--; return; } if (*line == ' ' || *line == '\0') leadbl(line); if (*line == '\0') put(line); else { /* main case */ pline = line; do { pline = getword(pline, wordbuf); if (pline) putword(wordbuf); } while (pline); } } usage() { fprintf(stderr, "usage: %s [ -z ] [ -wn ] [ -s start_string ] [ -e end_string] [ file ... ]\n", progname); } SHAR_EOF if test 3468 -ne "`wc -c < 'fmtr.c'`" then echo shar: error transmitting "'fmtr.c'" '(should have been 3468 characters)' fi chmod +x 'fmtr.c' fi echo shar: extracting "'fmtr.h'" '(365 characters)' if test -f 'fmtr.h' then echo shar: will not over-write existing file "'fmtr.h'" else cat << \SHAR_EOF > 'fmtr.h' #include #include int z_flag; /* look behind zero width spaces? */ int nf_val; /* no fill on or off? */ int ul_val; /* number of lines to underline */ int ce_val; /* number of lines to center */ int llength; char outbuf[BUFSIZ]; /* output buffer, ridiculously large */ char *outp; /* pointer into outbuf */ void n_brk(); char *strcpy(); SHAR_EOF if test 365 -ne "`wc -c < 'fmtr.h'`" then echo shar: error transmitting "'fmtr.h'" '(should have been 365 characters)' fi chmod +x 'fmtr.h' fi echo shar: extracting "'getopt.3'" '(2749 characters)' if test -f 'getopt.3' then echo shar: will not over-write existing file "'getopt.3'" else cat << \SHAR_EOF > 'getopt.3' .TH GETOPT 3 .DA 25 March 1982 .SH NAME getopt \- get option letter from argv .SH SYNOPSIS .ft B int getopt(argc, argv, optstring) .br int argc; .br char **argv; .br char *optstring; .sp extern char *optarg; .br extern int optind; .ft .SH DESCRIPTION .I Getopt returns the next option letter in .I argv that matches a letter in .IR optstring . .I Optstring is a string of recognized option letters; if a letter is followed by a colon, the option is expected to have an argument that may or may not be separated from it by white space. .I Optarg is set to point to the start of the option argument on return from .IR getopt . .PP .I Getopt places in .I optind the .I argv index of the next argument to be processed. Because .I optind is external, it is normally initialized to zero automatically before the first call to .IR getopt . .PP When all options have been processed (i.e., up to the first non-option argument), .I getopt returns .BR EOF . The special option .B \-\- may be used to delimit the end of the options; .B EOF will be returned, and .B \-\- will be skipped. .SH SEE ALSO getopt(1) .SH DIAGNOSTICS .I Getopt prints an error message on .I stderr and returns a question mark .RB ( ? ) when it encounters an option letter not included in .IR optstring . .SH EXAMPLE The following code fragment shows how one might process the arguments for a command that can take the mutually exclusive options .B a and .BR b , and the options .B f and .BR o , both of which require arguments: .PP .RS .nf main(argc, argv) int argc; char **argv; { int c; extern int optind; extern char *optarg; \&. \&. \&. while ((c = getopt(argc, argv, "abf:o:")) != EOF) switch (c) { case 'a': if (bflg) errflg++; else aflg++; break; case 'b': if (aflg) errflg++; else bproc(); break; case 'f': ifile = optarg; break; case 'o': ofile = optarg; break; case '?': default: errflg++; break; } if (errflg) { fprintf(stderr, "Usage: ..."); exit(2); } for (; optind < argc; optind++) { \&. \&. \&. } \&. \&. \&. } .RE .PP A template similar to this can be found in .IR /usr/pub/template.c . .SH HISTORY Written by Henry Spencer, working from a Bell Labs manual page. Behavior believed identical to the Bell version. .SH BUGS It is not obvious how `\-' standing alone should be treated; this version treats it as a non-option argument, which is not always right. .PP Option arguments are allowed to begin with `\-'; this is reasonable but reduces the amount of error checking possible. .PP .I Getopt is quite flexible but the obvious price must be paid: there is much it could do that it doesn't, like checking mutually exclusive options, checking type of option arguments, etc. SHAR_EOF if test 2749 -ne "`wc -c < 'getopt.3'`" then echo shar: error transmitting "'getopt.3'" '(should have been 2749 characters)' fi chmod +x 'getopt.3' fi echo shar: extracting "'getopt.c'" '(1608 characters)' if test -f 'getopt.c' then echo shar: will not over-write existing file "'getopt.c'" else cat << \SHAR_EOF > 'getopt.c' #ifndef lint static char rcsid[] = "$Header: getopt.c,v 1.3 86/05/05 14:20:35 root Exp $"; #endif /* got this off net.sources */ #include /* * get option letter from argument vector */ int opterr = 1, /* if set to zero no message for bad option */ optind = 1, /* index into parent argv vector */ optopt; /* character checked for validity */ char *optarg; /* argument associated with option */ #define BADCH (int)'?' #define EMSG "" getopt(nargc,nargv,ostr) int nargc; char **nargv, *ostr; { static char *place = EMSG; /* option letter processing */ register char *oli; /* option letter list index */ char *index(); if(!*place) { /* update scanning pointer */ if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF); if (*place == '-') { /* found "--" */ ++optind; return(EOF); } } /* option letter okay? */ if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) { if(!*place) ++optind; if (opterr) fprintf(stderr, "%s: illegal option -- %c\n", *nargv, optopt); return(BADCH); } if (*++oli != ':') { /* don't need argument */ optarg = NULL; if (!*place) ++optind; } else { /* need an argument */ if (*place) optarg = place; /* no white space */ else if (nargc <= ++optind) { /* no arg */ place = EMSG; if (opterr) fprintf(stderr, "%s: option requires an argument -- %c\n", *nargv, optopt); return(BADCH); } else optarg = nargv[optind]; /* white space */ place = EMSG; ++optind; } return(optopt); /* dump back option letter */ } SHAR_EOF if test 1608 -ne "`wc -c < 'getopt.c'`" then echo shar: error transmitting "'getopt.c'" '(should have been 1608 characters)' fi chmod +x 'getopt.c' fi echo shar: extracting "'lowtext.c'" '(2420 characters)' if test -f 'lowtext.c' then echo shar: will not over-write existing file "'lowtext.c'" else cat << \SHAR_EOF > 'lowtext.c' #ifndef lint static char rcsid[] = "$Header: lowtext.c,v 1.3 86/04/25 17:15:14 root Exp $"; #endif #include "fmtr.h" int ti_val; /* leadbl() deals with leading blanks, causes break, then sets * ti_val to number of blanks, unless line is blank. Then pulls * line forewards, so text() now has line starting with text, with * ti_val containing the needed indent. */ leadbl(line) char *line; { char *ip; n_brk(); ip = line; while (*ip == ' ') ip++; if (*ip != '\n') ti_val += ip - line; (void) strcpy(line, ip); } /* n_brk() causes a break */ void n_brk() { if (outp > outbuf) { outp--; /* back off from EOS */ while (*outp == ' ' && outp >= outbuf) /* remove trailing blanks */ outp--; *++outp = '\0'; put(outbuf); } outp = outbuf; } /* * getword gets the next word plus trailing space * from the array pointed to by pline and * returns it in that pointed at by word. If there are * no further words on that line, it returns NULL. * It returns a pointer to the start of the next word. */ char *getword(pline, word) char *pline, *word; { if (*pline == '\0') { *word = '\0'; return(NULL); } while (*pline != ' ' && *pline != '\0') { if (*pline == '\\' && isspace(pline[1])) /* get escaped space in word */ *word++ = *pline++; *word++ = *pline++; } /* get trailing spaces, and guarantee spaces at end of line; * normally one but two at end of sentence. */ if (*pline == '\0') { char *cptmp = pline; *word++ = ' '; while (any(*--cptmp, "\"']})")) ; if (any(*cptmp, ".:!?")) *word++ = ' '; } while (*pline == ' ') *word++ = *pline++; *word = '\0'; return(pline); } putword(word) /* put word into output buffer */ char *word; { int s, t; /* not needed, but greatly simplify if */ t = strlen(word) - 1; /* -1 for one trailing blank */ s = outp - outbuf; if (s + t <= llength - ti_val) { for (; *word; *outp++ = *word++) ; return; } n_brk(); for (; *word; *outp++ = *word++) ; } put(line) /* output routine, separate as is more complex in original */ char *line; { int i; for (i = 1; i <= ti_val; i++) putchar(' '); ti_val = 0; puts(line); } any(ch, string) /* returns true if character is in string */ char ch; char *string; { while (*string) if (ch == *string++) return(1); return(0); } SHAR_EOF if test 2420 -ne "`wc -c < 'lowtext.c'`" then echo shar: error transmitting "'lowtext.c'" '(should have been 2420 characters)' fi chmod +x 'lowtext.c' fi exit 0 # End of shell archive