brachman@ubc-cs.UUCP (Barry Brachman) (04/30/86)
Here's a program that converts ASCII text files to PostScript for printing on an Apple LaserWriter. See the README file for further info. Enjoy. Barry Brachman - University of British Columbia ----- CUT HERE ----- CUT HERE ----- CUT HERE ----- CUT HERE ----- CUT HERE : This is a shar archive. Extract with sh, not csh. : This archive ends with exit, so do not worry about trailing junk. echo 'Extracting README' sed 's/^X//' > README << '+ END-OF-FILE README' X XHere's a program that reads ASCII text files and produces PostScript output Xsuitable for printing on an Apple LaserWriter. XThe program has been in use here for several months on both Vaxen and SUNs Xrunning 4.2. X XDisclaimer section { X XThere are some 4.2 dependencies in the code, but (hopefully) not too many. XThe code is not bulletproof and I've taken some shortcuts such as emitting XPostScript from various places. XAlso, since I didn't want to fool around with PostScript anymore than was Xnecessary, lwf produces more output than is necessary. XIf more of the processing were done on the LaserWriter then the volume Xcould be reduced significantly. XStill, I wanted to keep it simple and I've found lwf to run without problem. X X} X XThere are five files: X X README This file X lwf.c LaserWriter filter to convert ASCII text to PostScript X range.c Routine to check if a number is in a given range X ranget.c A program to test out range.c X lwf.1 Manual page for lwf X XTo compile: X cc -o lwf lwf.c range.c X cc -o ranget ranget.c range.c X XI used the ranget program for debugging the inrange and checkrange Xfunctions; you may want to use it to see how ranges work. X XI hope you find lwf to be useful. XIf you come across any bugs please let me know. X XP.S. XI've modified dvi2ps to use range.c so that more general page ranges may Xbe specified. The changes to dvi2ps are rather small and I don't think Xyou'll have any trouble making them. X X----- XBarry Brachman XDept. of Computer Science XUniv. of British Columbia XVancouver, B.C. V6T 1W5 X X.. {ihnp4!alberta, uw-beaver}!ubc-vision!ubc-cs!brachman Xbrachman@cs.ubc.cdn Xbrachman%ubc.csnet@csnet-relay.arpa Xbrachman@ubc.csnet + END-OF-FILE README chmod 'u=rw,g=r,o=r' 'README' echo ' -rw-r--r-- 1 brachman 1659 Apr 30 12:24 README (as sent)' echo -n ' ' /bin/ls -l README echo 'Extracting lwf.1' sed 's/^X//' > lwf.1 << '+ END-OF-FILE lwf.1' X.TH LWF 1-LOCAL "16 January 1986" X.UC X.SH NAME Xlwf \- LaserWriter filter X.SH SYNOPSIS X.B lwf X[-d] [-h] [-i#] [-l] [-olist] [-p] [-s#] [-v] [file ...] X.SH DESCRIPTION X.I Lwf Xtakes one or more ASCII text files as input and produces PostScript Xinstructions that may be sent to a LaserWriter (see X.B lpr(1)) Xto print the files. XIf no files are given on the command line, the standard input is used. XThe page order is automatically reversed so that the pages appear in Xthe correct sequence in the output tray of the LaserWriter. X.sp 2 XNormally a header page is printed to separate jobs; the X.B -h Xflag prevents this. XThe X.B -l Xflag indicates that landscape format is to be used instead of the default Xportrait format. XLines may be shifted to the right using the X.B -i Xflag. XThis flag is immediately followed by the distance in inches to shift Xall text from the left margin instead of the default amount (about 1/3 inch); Xthe resolution is approximately 1/72 of an inch. XThe X.B -p Xflag indicates that X.B pr(1) Xis to be used to perform pagination and print page headers. XThe X.B -d Xand X.B -v Xflags are used for debugging. X.sp 2 XThe default point size is 10. XThe X.B -s Xflag, immediately followed by a 7, 8, 9, 10, 11, or 12 causes the corresponding Xpoint size to be used. XCourier font is used. XThe program correctly handles the form feed character and tabs. XThe program also understands backspacing; backspaces followed by underscores Xmay be used to underline. X.PP XThe X.B -o Xflag is immediately followed by a range specification; Xonly pages whose page numbers appear in the comma-separated list of numbers Xand ranges will be printed. XA range N:M means pages N through M inclusive; an initial :N means from Xthe beginning to page N; and a final N: means from N to the end. XThe default, ``:'', is to print all pages. X.SH NOTE XWhen using X.B lwf Xwith X.B nroff(1) or pr(1) Xit may be necessary to know the number of lines per page: X.br X.in +5 X.sp 2 X.nf XPoint Size Portrait Landscape X Lines/Columns Lines/Columns X X 7 108/135 80/181 X 8 94/118 70/159 X 9 84/105 62/141 X 10 75/94 56/127 X 11 68/86 51/115 X 12 62/79 46/106 X.in -5 X.fi X.SH EXAMPLE XThe command X.sp 2 X.ti +5 Xlwf -p -l -s12 -i1.5 file1.c file2.c | lpr -Plw X.sp 2 Xwould print the two files on the LaserWriter in landscape format with Xpage headings, indenting 1.5 inches, and in 12 point type. X.sp 1 XThe command X.sp 2 X.ti +5 Xpr -w162 -2 -l81 foo.c | lwf -i0.2 -h -l -s7 | lpr -Plw X.sp 2 Xwould print foo.c in two column form with page headings, indenting 0.2 inches Xin landscape format using 7 point type. XNo header page would be printed. X.SH SEE ALSO Xpr(1), lpr(1), nroff(1) X.SH BUGS XIf the specified indent is too small for the font size being used Xthe first few characters on a line may be lost. XLines that are too long are silently truncated. XThe program can only handle the 6 different point sizes and the single font. XThe volume of output is much greater than the volume of input. X.SH AUTHOR XBarry Brachman X.br XThe 'only' page idea is borrowed from nroff. + END-OF-FILE lwf.1 chmod 'u=rw,g=r,o=r' 'lwf.1' echo ' -rw-r--r-- 1 brachman 3065 Apr 30 10:55 lwf.1 (as sent)' echo -n ' ' /bin/ls -l lwf.1 echo 'Extracting lwf.c' sed 's/^X//' > lwf.c << '+ END-OF-FILE lwf.c' X X/* 456789a123456789b123456789c123456789d123456789e123456789f123456789g1234567*/ X X/* X * lwf - print a text file on the LaserWriter X * X * Usage: lwf [-d] [-h] [-i#] [-l] [-olist] [-p] [-s#] [-v] [file ...] X * X * Options: X * -d Debug mode X * -h Don't print header page X * -i# Indent each line X * -l Landscape instead of PORTRAIT X * -olist Only print pages in the specified range X * -p Use pr to print X * -s# Use point size # X * -v Verbose X * X * If no files are specified, stdin is used. X * Page reversal is performed. X * Form feeds should work X * Backspacing with underlining (or overprinting) should work as expected X * X * Problems: X * - Should have: X * [-#n] option, where n=number of copies of each file or stdin X * [-T filename] option, where filename is used for title instead X * of a blank name X * - User can't give pr header if stdin is used X * - The volumne of PostScript output produced by this program could be X * reduced somewhat (there may also be do-nothing sequences produced) X * X * BJB - Nov/85 X * ======================================================================== X * X * Copyright (c) Barry Brachman X * X * Permission is given to freely copy or distribute this program X * with the following restrictions: X * X * 1) The author is not responsible for the consequences of use of X * this software and is not responsible for correcting any defects X * 2) The origin of this software must not be misrepresented, either by X * explicit claim or by omission X * 3) You may not sell this program X * 4) Altered versions must be plainly marked as such, and must not X * be misrepresented as being the original software X * X * X * Barry Brachman X * Dept. of Computer Science X * Univ. of British Columbia X * Vancouver, B.C. V6T 1W5 X * X * .. {ihnp4!alberta, uw-beaver}!ubc-vision!ubc-cs!brachman X * brachman@cs.ubc.cdn X * brachman%ubc.csnet@csnet-relay.arpa X * brachman@ubc.csnet X * ======================================================================== X */ X X#include <sys/file.h> X#include <pwd.h> X#include <stdio.h> X X#define PRCMD "/bin/pr" /* where pr is */ X X#define MAXPAGES 10000 /* maximum number of pages */ X#define PORTRAIT_START_Y 768 /* first row (Y coord) on each page */ X#define LANDSCAPE_START_Y 576 X#define START_X 25 /* position of start of each line */ X#define START_Y_HEADER 600 /* first row (Y coord) of header */ X X#define MAX_X 612 X#define MAX_Y 792 X X#define PS_INTR 03 X#define PS_EOF 04 X X#define NPSIZES 6 Xstruct psize { X int size; X int portrait_page_length; X int landscape_page_length; X} psize[NPSIZES] = { X 7, 108, 80, X 8, 94, 70, X 9, 84, 62, X 10, 75, 56, X 11, 68, 51, X 12, 62, 46 X}; X X#define USAGE "[-d] [-h] [-i#] [-l] [-olist] [-p] [-s#] [-v] [file ...]" X Xlong page_map[MAXPAGES]; /* offset of first byte of each page */ Xint page_count; X Xint lines_per_page; Xint point_size; Xint start_x, start_y; X Xchar bufin[BUFSIZ]; Xchar bufout[10240]; /* should be plenty big enough */ Xchar bigbuf[32768]; /* max page size */ X Xint currentp, row; Xchar *range; X Xint dflag, hflag, lflag, pflag, vflag; X Xchar *fgetline(); X Xchar *progname; X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X register int i, j, first_file; X char *pc; X struct psize *p, *get_psize(); X double offset, atof(); X char *rindex(); X FILE *infile, *popen(); X X if ((pc = rindex(argv[0], '/')) != NULL) X progname = pc + 1; X else X progname = argv[0]; X range = ":"; X hflag = 1; X start_x = START_X; X p = get_psize(10); X for (i = 1; i < argc && argv[i][0] == '-'; i++) { X switch (argv[i][1]) { X case 'd': X dflag = 1; X break; X case 'h': X hflag = 0; X break; X case 'i': X offset = atof(&argv[i][2]); X if (offset < 0.0 || offset >= 8.5) { X fprintf(stderr, "%s: bad indent\n", progname); X exit(1); X } X start_x = (int) (offset * 72); X break; X case 'l': X lflag = 1; X break; X case 'o': X range = &argv[i][2]; X if (checkrange(range)) { X fprintf(stderr, "%s: bad range specification\n", X progname); X exit(1); X } X break; X case 'p': X pflag = 1; X break; X case 's': X j = atoi(&argv[i][2]); X if ((p = get_psize(j)) == (struct psize *) NULL) { X fprintf(stderr, "%s: bad point size\n", progname); X exit(1); X } X break; X case 'v': X vflag = 1; X break; X default: X fprintf(stderr, "Usage: %s %s\n", progname, USAGE); X exit(1); X } X } X X for (j = i; j < argc; j++) { X if (access(argv[j], R_OK) == -1) { X fprintf(stderr, "%s: cannot access %s\n", progname, X argv[j]); X exit(1); X } X } X X if (lflag) { X start_y = LANDSCAPE_START_Y; X lines_per_page = p->landscape_page_length; X } X else { X start_y = PORTRAIT_START_Y; X lines_per_page = p->portrait_page_length; X } X point_size = p->size; X X preamble(); X X first_file = i; X if (i == argc) { /* no files on command line */ X infile = stdin; X if (pflag) { X build_prcmd(bufin, ""); X if ((infile = popen(bufin, "r")) == (FILE *) NULL) { X fprintf(stderr, "%s: can't popen\n", progname); X exit(1); X } X } X if (vflag) X fprintf(stderr, "printing stdin\n"); X print(infile); X if (pflag) X pclose(infile); X } X X for (j = argc - 1; i < argc; j--) { X infile = stdin; X if (pflag) { X build_prcmd(bufin, argv[j]); X if ((infile = popen(bufin, "r")) == (FILE *) NULL) { X fprintf(stderr, "%s: can't popen\n", progname); X exit(1); X } X } X else { X if (freopen(argv[j], "r", stdin) == NULL) { X fprintf(stderr, "%s: can't open %s\n", X progname, argv[j]); X exit(1); X } X } X if (vflag) X fprintf(stderr, "printing %s\n", argv[j]); X print(infile); X if (pflag) X pclose(infile); X i++; X } X X if (hflag) X header(argc - first_file, argv + first_file); X printf("grestore initgraphics %c\n", PS_EOF); X} X X/* X * Return a pointer to the point size structure for the X * specified point size X */ Xstruct psize * Xget_psize(size) Xint size; X{ X register int i; X X for (i = 0; i < NPSIZES; i++) X if (psize[i].size == size) X break; X if (i == NPSIZES) X return((struct psize *) NULL); X return(&psize[i]); X} X X/* X * Initial lines sent to the LaserWriter X */ Xpreamble() X{ X X printf("%c initgraphics gsave\n", PS_EOF); X printf("/Courier findfont %d scalefont\nsetfont\n", point_size); X printf("/bkup { stringwidth pop neg 0 rmoveto } def\n"); X printf("/tab1 {( ) show} def\n"); X printf("/tab2 {( ) show} def\n"); X printf("/tab3 {( ) show} def\n"); X printf("/tab4 {( ) show} def\n"); X printf("/tab5 {( ) show} def\n"); X printf("/tab6 {( ) show} def\n"); X printf("/tab7 {( ) show} def\n"); X printf("/tab8 {( ) show} def\n"); X if (lflag) X printf("612 0 translate\n90 rotate\n"); X fflush(stdout); X} X X/* X * Generate a command, in the specified buffer, to print the given file X * according to the options in effect X */ Xbuild_prcmd(buf, file) Xchar *buf, *file; X{ X sprintf(buf, "%s -l%d %s", PRCMD, lines_per_page, file); X if (vflag) X fprintf(stderr, "pr cmd: %s\n", buf); X} X X/* X * Print a file X * X * The input stream may be stdin, a file, or a pipe X * The output goes to a temporary file, and then reverse is called X * to do the page reversal to stdout X */ Xprint(infile) XFILE *infile; X{ X register int eof, pagenum, r; X register char *p; X FILE *outfile; X char *gets(), *mktemp(); X X page_count = 0; X page_map[0] = 0L; X sprintf(bufin, "/tmp/%sXXXXXX", progname); X if (vflag) X fprintf(stderr, "temp will be: %s ... ", bufin); X p = mktemp(bufin); X if (vflag) X fprintf(stderr, "%s\n", p); X if ((outfile = fopen(p, "w+")) == (FILE *) NULL) { X fprintf(stderr, "%s: can't create %s\n", progname, p); X cleanup(); X /*NOTREACHED*/ X } X if (!dflag) X unlink(p); X else X fprintf(stderr, "will not unlink %s\n", p); X pagenum = 1; X eof = 0; X while (!eof) { X row = start_y; X if ((r = inrange(pagenum, range)) == -1) { X cleanup(); X /*NOTREACHED*/ X } X else if (r == 1) X eof = printpage(infile, outfile); X else if (r == 0) X eof = flushpage(infile); X else { X fprintf(stderr, "%s: bad inrange result\n", progname); X cleanup(); X /*NOTREACHED*/ X } X pagenum++; X } X if (row != start_y) X endpage(outfile); X fflush(outfile); X reverse(outfile); X fclose(outfile); X} X X/* X * Process the next page X */ Xprintpage(infile, outfile) XFILE *infile, *outfile; X{ X register int lineno; X X for (lineno = 0; lineno < lines_per_page; lineno++) { X if (fgetline(bufin, sizeof(bufin), infile) == (char *) NULL) X return(1); X if (bufin[0] == '\f') X break; X if (bufin[0] != '\n') { X fprintf(outfile, "%d %d moveto\n", start_x, row); X proc(bufin, outfile); X fprintf(outfile, "(%s) show\n", bufout); X } X row -= point_size; X } X endpage(outfile); X return(0); X} X X/* X * The next page will not be printed; just consume the input and discard X * Don't change xrow since we don't want an endpage() X */ Xflushpage(infile) XFILE *infile; X{ X register int lineno, xrow; X X xrow = row; X for (lineno = 0; lineno < lines_per_page; lineno++) { X if (fgetline(bufin, sizeof(bufin), infile) == (char *) NULL) X return(1); X if (bufin[0] == '\f') X break; X xrow -= point_size; X } X return(0); X} X X/* X * A page has been written to the temp file X * Record the start of the next page X */ Xendpage(outfile) XFILE *outfile; X{ X long ftell(); X X if (page_count == MAXPAGES) { X fprintf(stderr, "%s: pagelimit (%d) reached\n", X progname, MAXPAGES); X cleanup(); X /*NOTREACHED*/ X } X fprintf(outfile, "copypage erasepage\n"); X fflush(outfile); X page_map[++page_count] = ftell(outfile); X if (vflag) X fprintf(stderr, "x"); X} X X/* X * Print the pages to stdout in reverse order X */ Xreverse(outfile) XFILE *outfile; X{ X register int fd, i, nbytes; X X if (vflag) X fprintf(stderr, "\nreversing %d page%s\n", page_count, X page_count > 1 ? "s" : ""); X fd = fileno(outfile); X if (dflag) { X for (i = 0; i <= page_count; i++) X fprintf(stderr, "[%ld]\n", page_map[i]); X } X for (i = page_count - 1; i >= 0; i--) { X if (lseek(fd, page_map[i], 0) == -1) { X fprintf(stderr, "%s: seek error\n", progname); X cleanup(); X /*NOTREACHED*/ X } X nbytes = page_map[i + 1] - page_map[i]; X if (nbytes > sizeof(bigbuf)) { X fprintf(stderr, "%s: page too big\n", progname); X fprintf(stderr, "nbytes = %d\n", nbytes); X cleanup(); X /*NOTREACHED*/ X } X nbytes = read(fd, bigbuf, nbytes); X write(1, bigbuf, nbytes); X } X} X X/* X * Process a line, escaping characters when necessary and handling tabs X * We are assuming that a line ends with a newline (fgets/fgetline) X * X * NOTE: lazy attempt at checking for buffer overflow on bufout X */ Xproc(in, outfile) Xchar *in; XFILE *outfile; X{ X register int i; X register char *last, *p, *q, *savep; X X currentp = 0; X last = bufout + sizeof(bufout); X for (p = in, q = bufout; *p != '\n' && *p != '\0' && q < last; p++) { X switch (*p) { X case '\r': X *q++ = '\\'; X *q++ = 'r'; X currentp = 0; X break; X case '\t': X/* X *q++ = '\\'; X *q++ = 't'; X*/ X i = 8 - (currentp % 8); X sprintf(q, ") show tab%d (", i); X/* sprintf(q, ") show (%*s) show (", i, ""); */ X q += strlen(q); X currentp += i; X break; X case '\b': X/* X *q++ = '\\'; X *q++ = 'b'; X*/ X savep = p++; X for (i = 1; *p == '\b'; p++) X i++; X *q = '\0'; X fprintf(outfile, "(%s) show\n", bufout); X fprintf(outfile, "(%.*s) bkup ", i, savep - i); X/* X fprintf(outfile, "(%.*s) stringwidth ", i, savep - i); X fprintf(outfile, "pop neg 0 rmoveto\n"); X*/ X currentp -= i; X q = bufout; X p--; X break; X case '\f': X/* X *q++ = '\\'; X *q++ = 'f'; X*/ X *q = '\0'; X fprintf(outfile, "(%s) show\n", bufout); X endpage(outfile); X row = start_y; X fprintf(outfile, "%d %d moveto\n", start_x, row); X q = bufout; X currentp = 0; X break; X case '\\': X *q++ = '\\'; X *q++ = '\\'; X currentp++; X break; X case '(': X *q++ = '\\'; X *q++ = '('; X currentp++; X break; X case ')': X *q++ = '\\'; X *q++ = ')'; X currentp++; X break; X default: X *q++ = *p; X currentp++; X break; X } X } X *q = '\0'; X if (q >= last) { X fprintf(stderr, "%s: bufout overflow\n", progname); X cleanup(); X /*NOTREACHED*/ X } X} X X/* X * Print a header page X */ Xheader(nfiles, files) Xint nfiles; Xchar **files; X{ X register int i; X register char *p; X long t, time(); X char *ctime(), *gethostname(), *getlogin(); X struct passwd *pw, *getpwuid(); X X if (vflag) { X fprintf(stderr, "printing header\n"); X fprintf(stderr, "%d file%s are:\n", nfiles, X nfiles > 1 ? "s" : ""); X if (nfiles == 0) X fprintf(stderr, "\tstdin\n"); X for (i = 0; i < nfiles; i++) X fprintf(stderr, "\t%s\n", files[i]); X } X printf("initgraphics\n"); X printf("/Times-Roman findfont 18 scalefont\nsetfont\n"); X row = START_Y_HEADER; X printf("newpath %d %d moveto\n", START_X, row); X printf("%d %d lineto\n", START_X + 400, row); X row -= 6; X printf("%d %d moveto\n", START_X, row); X printf("%d %d lineto\n", START_X + 400, row); X row -= 24; X printf("2 setlinewidth\n"); X printf("stroke\n"); X X if ((p = getlogin()) == (char *) NULL) { X if ((pw = getpwuid(getuid())) == NULL) X p = "Whoknows"; X else X p = pw->pw_name; X endpwent(); X } X printf("%d %d moveto\n", START_X, row); X printf("(User: %s) show\n", p); X row -= 24; X gethostname(bufin, sizeof(bufin)); X printf("%d %d moveto\n", START_X, row); X printf("(Host: %s) show\n", bufin); X row -= 24; X t = time(0); X p = ctime(&t); X p[strlen(p) - 1] = '\0'; X printf("%d %d moveto\n", START_X, row); X printf("(Date: %s) show\n", p); X row -= 24; X X if (nfiles == 0) { X printf("%d %d moveto\n", START_X, row); X printf("(File: stdin) show\n", bufout); X } X else { X printf("%d %d moveto\n", START_X, row); X for (i = 0, p = bufin; i < nfiles; i++) { X sprintf(p, "%s%s", files[i], X i != nfiles - 1 ? ", " : ""); X p += strlen(p); X } X proc(bufin, stdout); X printf("(File%s: %s) show\n", nfiles > 1 ? "s" : "", bufout); X } X X row -= 12; X printf("newpath %d %d moveto\n", START_X, row); X printf("%d %d lineto\n", START_X + 400, row); X row -= 6; X printf("%d %d moveto\n", START_X, row); X printf("%d %d lineto\n", START_X + 400, row); X printf("stroke\n"); X printf("showpage\n"); X} X X/* X * Special version of fgets X * Read until a formfeed or a newline X * If a formfeed is the first character, return it immediately X * If a formfeed is found after the first character, replace it by a newline X * and push the formfeed back onto the input stream X * A special case is a formfeed followed by a newline in which case the X * newline is ignored X */ Xchar * Xfgetline(s, n, iop) Xchar *s; Xint n; Xregister FILE *iop; X{ X register int ch; X register char *cs; X X cs = s; X if (--n > 0) { X if ((ch = getc(iop)) == EOF) X return(NULL); X if (ch == '\f') { X if ((ch = getc(iop)) != '\n') X if (ungetc(ch, iop) == EOF) X fprintf(stderr, "fgetline - ungetc\n"); X strncpy(cs, "\f\n", n); X return(s); X } X } X while (n-- > 0) { X if (ch == '\f') X break; X *cs++ = ch; X if (ch == '\n') X break; X if ((ch = getc(iop)) == EOF) X break; X } X X if (ch == EOF && cs == s) X return(NULL); X if (ch == '\f') { X *cs++ = '\n'; X if (ungetc(ch, iop) == EOF) X fprintf(stderr, "fgetline can't ungetc\n"); X } X *cs++ = '\0'; X return(s); X} X X/* X * Clean up and exit after an error X */ Xcleanup() X{ X X exit(1); X} + END-OF-FILE lwf.c chmod 'u=rw,g=r,o=r' 'lwf.c' echo ' -rw-r--r-- 1 brachman 14940 Apr 30 11:05 lwf.c (as sent)' echo -n ' ' /bin/ls -l lwf.c echo 'Extracting range.c' sed 's/^X//' > range.c << '+ END-OF-FILE range.c' X X#include <ctype.h> X#include <stdio.h> X X/* X * Return 1 if the given number is in the specified range, X * -1 if there is an error in the range specification, X * 0 otherwise X * X * Ranges have a format *similar* to that used by [nt]roff; i.e., X * a comma separated list of specifiers of the form: X * 1) n means x such that x = n X * 2) :n means all x such that x <= n X * 3) n: means all x such that x >= n X * 4) n:m means all x such that n <= x <= m X * 5) : means all x X * n is an integer X * X * Problems: X * The routine prints an error message if the range is strange - this might X * not always be desirable. X * X * I've left the error checking in inrange; it is redundant (I hope!) X * Nov/85 BJB X * ======================================================================== X * X * Copyright (c) Barry Brachman X * X * Permission is given to freely copy or distribute this program X * with the following restrictions: X * X * 1) The author is not responsible for the consequences of use of X * this software and is not responsible for correcting any defects X * 2) The origin of this software must not be misrepresented, either by X * explicit claim or by omission X * 3) You may not sell this program X * 4) Altered versions must be plainly marked as such, and must not X * be misrepresented as being the original software X * X * X * Barry Brachman X * Dept. of Computer Science X * Univ. of British Columbia X * Vancouver, B.C. V6T 1W5 X * X * .. {ihnp4!alberta, uw-beaver}!ubc-vision!ubc-cs!brachman X * brachman@cs.ubc.cdn X * brachman%ubc.csnet@csnet-relay.arpa X * brachman@ubc.csnet X * ======================================================================== X */ X X#define SEP_CHAR ':' X#define BAD_CHAR '?' X Xinrange(num, range) Xint num; Xchar *range; X{ X char *p; X int type1, type2, val1, val2; X X if (checkrange(range)) X return(-1); X type1 = type2 = 0; X p = range; X while (1) { X val1 = getnum(&p, &type1); X if (type1 == BAD_CHAR) { X fprintf(stderr, "Bad first number in range\n"); X return(-1); X } X X switch (*p) { X case ',': X if (type1 == SEP_CHAR) /* just a colon */ X return(1); X if (num == val1) /* plain number */ X return(1); X p++; X continue; X case ':': X p++; X if (*p == ',' || *p == '\0') { /* no rhs */ X if (type1 == SEP_CHAR) X return(1); X if (num >= val1) X return(1); X if (*p == '\0') X return(0); X p++; X break; X } X X val2 = getnum(&p, &type2); X if (type2 == BAD_CHAR) { X fprintf(stderr, "Bad second number in range\n"); X return(-1); X } X X if (val1 > val2) { X fprintf(stderr, "Range values reversed\n"); X return(-1); X } X if (type1 == SEP_CHAR && num <= val2) X return(1); X if (num >= val1 && num <= val2) X return(1); X if (*p == '\0') X return(0); X if (*p == ',') X p++; X break; X case '\0': X if (val1 == num) X return(1); X return(0); X default: X fprintf(stderr, "Bad character in range\n"); X return(-1); X } X } X} X Xcheckrange(range) Xchar *range; X{ X char *p; X int type1, type2, val1, val2; X X type1 = type2 = 0; X p = range; X while (1) { X val1 = getnum(&p, &type1); X if (type1 == BAD_CHAR) { X fprintf(stderr, "Bad first number in range\n"); X return(-1); X } X X switch (*p) { X case ',': X p++; X continue; X case ':': X p++; X if (*p == ',' || *p == '\0') { X if (*p == '\0') X return(0); X p++; X break; X } X X val2 = getnum(&p, &type2); X if (type2 == BAD_CHAR) { X fprintf(stderr, "Bad second number in range\n"); X return(-1); X } X X if (val1 > val2) { X fprintf(stderr, "Range values reversed\n"); X return(-1); X } X if (*p == '\0') X return(0); X if (*p == ',') X p++; X break; X case '\0': X return(0); X default: X fprintf(stderr, "Bad character in range\n"); X return(-1); X } X } X} X Xstatic Xgetnum(pp, type) Xchar **pp; Xint *type; X{ X register int sign, val; X register char *p; X X p = *pp; X if (!isdigit(*p) && *p != '-') { X if (*p == SEP_CHAR) X *type = SEP_CHAR; X else X *type = BAD_CHAR; X return(0); X } X sign = 1; X if (*p == '-') { X sign = -1; X p++; X } X if (!isdigit(*p)) { X *type = BAD_CHAR; X return(0); X } X for (val = 0; isdigit(*p) && *p != '\0'; p++) X val = val * 10 + *p - '0'; X if (*p != '\0' && *p != ',' && *p != SEP_CHAR) { X *type = BAD_CHAR; X return(0); X } X *pp = p; X return(sign * val); X} + END-OF-FILE range.c chmod 'u=rw,g=r,o=r' 'range.c' echo ' -rw-r--r-- 1 brachman 4256 Apr 30 11:24 range.c (as sent)' echo -n ' ' /bin/ls -l range.c echo 'Extracting ranget.c' sed 's/^X//' > ranget.c << '+ END-OF-FILE ranget.c' X X/* X * Program to test the range routine X * X * To reuse the previous range specification just type <return> to the prompt X * X * BJB X */ X X#include <stdio.h> X Xchar buf[BUFSIZ], range[BUFSIZ]; X Xmain() X{ X register int i; X char *gets(); X X buf[0] = range[0] = '\0'; X while (1) { X printf("range? "); X if (gets(buf) == (char *) NULL) X break; X if (buf[0] != '\0') X strcpy(range, buf); X if (checkrange(range)) X continue; X printf("value? "); X if (gets(buf) == (char *) NULL) { X printf("\n"); X exit(0); X } X i = inrange(atoi(buf), range); X if (i == 0) X printf("\tno\n"); X else if (i == 1) X printf("\tyes\n"); X else if (i == -1) X printf("\terror\n"); X else X printf("\tbad result\n"); X } X printf("\n"); X} + END-OF-FILE ranget.c chmod 'u=rw,g=r,o=r' 'ranget.c' echo ' -rw-r--r-- 1 brachman 720 Apr 30 11:21 ranget.c (as sent)' echo -n ' ' /bin/ls -l ranget.c exit 0