[comp.sources.unix] v22i012: The BSD checknr program -- "lint for ?roff documents"

rsalz@uunet.uu.net (Rich Salz) (05/04/90)

Submitted-by: Keith Bostic <bostic%okeeffe.Berkeley.EDU@ucbvax.berkeley.edu>
Posting-number: Volume 22, Issue 12
Archive-name: checknr

[ I asked Keith for a copy of checknr that could be posted, and he was
  happy to give the following reply.  --r$  ]

It's definitely available, it just wasn't marked when uunet got their copy.

--keith

This program checks for matching font changes, size changes, matching
commands like .DS/.DE, .FS/.FE, etc.  As the manpage suggests, think of
it as lint for your nroff/troff documents.
	/r$

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	Makefile
#	checknr.1
#	checknr.c
#
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
X#
X# Copyright (c) 1988 Regents of the University of California.
X# All rights reserved.
X#
X# Redistribution and use in source and binary forms are permitted
X# provided that the above copyright notice and this paragraph are
X# duplicated in all such forms and that any documentation, advertising
X# materials, and other materials related to such redistribution and
X# use acknowledge that the software was developed by the University
X# of California, Berkeley.  The name of the University may not be
X# used to endorse or promote products derived from this software
X# without specific prior written permission.  THIS SOFTWARE IS PROVIDED
X# ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
X# WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND
X# FITNESS FOR A PARTICULAR PURPOSE.
X#
X# @(#)Makefile	5.2 (Berkeley) 5/11/89
X#
X
XCFLAGS=	-O
XLIBC=	/lib/libc.a
XSRCS=	checknr.c
XOBJS=
XMAN=	checknr.0
X
Xall: checknr
X
Xchecknr: ${LIBC}
X	${CC} -o $@ ${CFLAGS} $@.c
X
Xclean:
X	rm -f ${OBJS} core checknr
X
Xcleandir: clean
X	rm -f ${MAN} tags .depend
X
Xdepend: ${SRCS}
X	mkdep -p ${CFLAGS} ${SRCS}
X
Xinstall: ${MAN}
X	install -s -o bin -g bin -m 755 checknr ${DESTDIR}/usr/bin
X	install -c -o bin -g bin -m 444 ${MAN} ${DESTDIR}/usr/man/cat1
X
Xlint: ${SRCS}
X	lint ${CFLAGS} ${SRCS}
X
Xtags: ${SRCS}
X	ctags ${SRCS}
END-of-Makefile
echo x - checknr.1
sed 's/^X//' >checknr.1 << 'END-of-checknr.1'
X.\" Copyright (c) 1980 The Regents of the University of California.
X.\" All rights reserved.
X.\"
X.\" Redistribution and use in source and binary forms are permitted
X.\" provided that the above copyright notice and this paragraph are
X.\" duplicated in all such forms and that any documentation,
X.\" advertising materials, and other materials related to such
X.\" distribution and use acknowledge that the software was developed
X.\" by the University of California, Berkeley.  The name of the
X.\" University may not be used to endorse or promote products derived
X.\" from this software without specific prior written permission.
X.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X.\"
X.\"	@(#)checknr.1	6.3 (Berkeley) 10/30/88
X.\"
X.TH CHECKNR 1 "October 30, 1988"
X.UC 4
X.SH NAME
Xchecknr \- check nroff/troff files
X.SH SYNOPSIS
X.B checknr
X[
X.B \-s
X] [
X.B \-f
X] [
X.BR \-a ".x1.y1.x2.y2. ... .xn.yn"
X] [
X.BR \-c ".x1.x2.x3 ... .xn"
X] [
X\fIfile\fP ...
X]
X.SH DESCRIPTION
X.I Checknr
Xchecks a list of
X.IR nroff (1)
Xor
X.IR troff (1)
Xinput files for certain kinds of errors
Xinvolving mismatched opening and closing delimiters
Xand unknown commands.
XIf no files are specified,
X.I checknr
Xchecks the standard input.
XDelimeters checked are:
X.IP (1)
XFont changes using \efx ... \efP.
X.IP (2)
XSize changes using \esx ... \es0.
X.IP (3)
XMacros that come in open ... close forms, for example,
Xthe .TS and .TE macros which must always come in pairs.
X.PP
X.I Checknr
Xknows about the
X.IR ms (7)
Xand
X.IR me (7)
Xmacro packages.
X.PP
XAdditional pairs of macros can be added to the list using the
X.B \-a
Xoption.
XThis must be followed by groups of six characters, each group defining
Xa pair of macros.
XThe six characters are
Xa period,
Xthe first macro name,
Xanother period,
Xand the second macro name.
XFor example, to define a pair .BS and .ES, use \-\fBa\fP.BS.ES
X.PP
XThe
X.B \-c
Xoption defines commands which would otherwise be complained about
Xas undefined.
X.PP
XThe
X.B \-f
Xoption requests
X.I checknr
Xto ignore \ef font changes.
X.PP
XThe
X.B \-s
Xoption requests
X.I checknr
Xto ignore \es size changes.
X.PP
X.I Checknr
Xis intended to be used on documents that are prepared with
X.I checknr
Xin mind, much the same as
X.I lint.
XIt expects a certain document writing style for \ef and \es commands,
Xin that each \efx must be terminated with \efP and
Xeach \esx must be terminated with \es0.
XWhile it will work to directly go into the next font or explicitly
Xspecify the original font or point size,
Xand many existing documents actually do this,
Xsuch a practice will produce complaints from
X.I checknr.
XSince it is probably better to use the \efP and \es0 forms anyway,
Xyou should think of this as a contribution to your document
Xpreparation style.
X.SH SEE\ ALSO
Xnroff(1), troff(1), checkeq(1), ms(7), me(7)
X.SH DIAGNOSTICS
XComplaints about unmatched delimiters.
X.br
XComplaints about unrecognized commands.
X.br
XVarious complaints about the syntax of commands.
X.SH BUGS
XThere is no way to define a 1 character macro name using
X.BR \-a .
X.br
XDoes not correctly recognize certain reasonable constructs,
Xsuch as conditionals.
END-of-checknr.1
echo x - checknr.c
sed 's/^X//' >checknr.c << 'END-of-checknr.c'
X/*
X * Copyright (c) 1980 The Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by the University of California, Berkeley.  The name of the
X * University may not be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1980 The Regents of the University of California.\n\
X All rights reserved.\n";
X#endif /* not lint */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)checknr.c	5.3 (Berkeley) 10/30/88";
X#endif /* not lint */
X
X/*
X * checknr: check an nroff/troff input file for matching macro calls.
X * we also attempt to match size and font changes, but only the embedded
X * kind.  These must end in \s0 and \fP resp.  Maybe more sophistication
X * later but for now think of these restrictions as contributions to
X * structured typesetting.
X */
X#include <stdio.h>
X#include <ctype.h>
X
X#define MAXSTK	100	/* Stack size */
X#define MAXBR	100	/* Max number of bracket pairs known */
X#define MAXCMDS	500	/* Max number of commands known */
X
X/*
X * The stack on which we remember what we've seen so far.
X */
Xstruct stkstr {
X	int opno;	/* number of opening bracket */
X	int pl;		/* '+', '-', ' ' for \s, 1 for \f, 0 for .ft */
X	int parm;	/* parm to size, font, etc */
X	int lno;	/* line number the thing came in in */
X} stk[MAXSTK];
Xint stktop;
X
X/*
X * The kinds of opening and closing brackets.
X */
Xstruct brstr {
X	char *opbr;
X	char *clbr;
X} br[MAXBR] = {
X	/* A few bare bones troff commands */
X#define SZ	0
X	"sz",	"sz",	/* also \s */
X#define FT	1
X	"ft",	"ft",	/* also \f */
X	/* the -mm package */
X	"AL",	"LE",
X	"AS",	"AE",
X	"BL",	"LE",
X	"BS",	"BE",
X	"DF",	"DE",
X	"DL",	"LE",
X	"DS",	"DE",
X	"FS",	"FE",
X	"ML",	"LE",
X	"NS",	"NE",
X	"RL",	"LE",
X	"VL",	"LE",
X	/* the -ms package */
X	"AB",	"AE",
X	"BD",	"DE",
X	"CD",	"DE",
X	"DS",	"DE",
X	"FS",	"FE",
X	"ID",	"DE",
X	"KF",	"KE",
X	"KS",	"KE",
X	"LD",	"DE",
X	"LG",	"NL",
X	"QS",	"QE",
X	"RS",	"RE",
X	"SM",	"NL",
X	"XA",	"XE",
X	"XS",	"XE",
X	/* The -me package */
X	"(b",	")b",
X	"(c",	")c",
X	"(d",	")d",
X	"(f",	")f",
X	"(l",	")l",
X	"(q",	")q",
X	"(x",	")x",
X	"(z",	")z",
X	/* Things needed by preprocessors */
X	"EQ",	"EN",
X	"TS",	"TE",
X	/* Refer */
X	"[",	"]",
X	0,	0
X};
X
X/*
X * All commands known to nroff, plus macro packages.
X * Used so we can complain about unrecognized commands.
X */
Xchar *knowncmds[MAXCMDS] = {
X"$c", "$f", "$h", "$p", "$s", "(b", "(c", "(d", "(f", "(l", "(q", "(t",
X"(x", "(z", ")b", ")c", ")d", ")f", ")l", ")q", ")t", ")x", ")z", "++",
X"+c", "1C", "1c", "2C", "2c", "@(", "@)", "@C", "@D", "@F", "@I", "@M",
X"@c", "@e", "@f", "@h", "@m", "@n", "@o", "@p", "@r", "@t", "@z", "AB",
X"AE", "AF", "AI", "AL", "AM", "AS", "AT", "AU", "AX", "B",  "B1", "B2",
X"BD", "BE", "BG", "BL", "BS", "BT", "BX", "C1", "C2", "CD", "CM", "CT",
X"D",  "DA", "DE", "DF", "DL", "DS", "DT", "EC", "EF", "EG", "EH", "EM",
X"EN", "EQ", "EX", "FA", "FD", "FE", "FG", "FJ", "FK", "FL", "FN", "FO",
X"FQ", "FS", "FV", "FX", "H",  "HC", "HD", "HM", "HO", "HU", "I",  "ID",
X"IE", "IH", "IM", "IP", "IX", "IZ", "KD", "KE", "KF", "KQ", "KS", "LB",
X"LC", "LD", "LE", "LG", "LI", "LP", "MC", "ME", "MF", "MH", "ML", "MR",
X"MT", "ND", "NE", "NH", "NL", "NP", "NS", "OF", "OH", "OK", "OP", "P",
X"P1", "PF", "PH", "PP", "PT", "PX", "PY", "QE", "QP", "QS", "R",  "RA",
X"RC", "RE", "RL", "RP", "RQ", "RS", "RT", "S",  "S0", "S2", "S3", "SA",
X"SG", "SH", "SK", "SM", "SP", "SY", "T&", "TA", "TB", "TC", "TD", "TE",
X"TH", "TL", "TM", "TP", "TQ", "TR", "TS", "TX", "UL", "US", "UX", "VL",
X"WC", "WH", "XA", "XD", "XE", "XF", "XK", "XP", "XS", "[",  "[-", "[0",
X"[1", "[2", "[3", "[4", "[5", "[<", "[>", "[]", "]",  "]-", "]<", "]>",
X"][", "ab", "ac", "ad", "af", "am", "ar", "as", "b",  "ba", "bc", "bd",
X"bi", "bl", "bp", "br", "bx", "c.", "c2", "cc", "ce", "cf", "ch", "cs",
X"ct", "cu", "da", "de", "di", "dl", "dn", "ds", "dt", "dw", "dy", "ec",
X"ef", "eh", "el", "em", "eo", "ep", "ev", "ex", "fc", "fi", "fl", "fo",
X"fp", "ft", "fz", "hc", "he", "hl", "hp", "ht", "hw", "hx", "hy", "i",
X"ie", "if", "ig", "in", "ip", "it", "ix", "lc", "lg", "li", "ll", "ln",
X"lo", "lp", "ls", "lt", "m1", "m2", "m3", "m4", "mc", "mk", "mo", "n1",
X"n2", "na", "ne", "nf", "nh", "nl", "nm", "nn", "np", "nr", "ns", "nx",
X"of", "oh", "os", "pa", "pc", "pi", "pl", "pm", "pn", "po", "pp", "ps",
X"q",  "r",  "rb", "rd", "re", "rm", "rn", "ro", "rr", "rs", "rt", "sb",
X"sc", "sh", "sk", "so", "sp", "ss", "st", "sv", "sz", "ta", "tc", "th",
X"ti", "tl", "tm", "tp", "tr", "u",  "uf", "uh", "ul", "vs", "wh", "xp",
X"yr", 0
X};
X
Xint	lineno;		/* current line number in input file */
Xchar	line[256];	/* the current line */
Xchar	*cfilename;	/* name of current file */
Xint	nfiles;		/* number of files to process */
Xint	fflag;		/* -f: ignore \f */
Xint	sflag;		/* -s: ignore \s */
Xint	ncmds;		/* size of knowncmds */
Xint	slot;		/* slot in knowncmds found by binsrch */
X
Xchar	*malloc();
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	FILE *f;
X	int i;
X	char *cp;
X	char b1[4];
X
X	/* Figure out how many known commands there are */
X	while (knowncmds[ncmds])
X		ncmds++;
X	while (argc > 1 && argv[1][0] == '-') {
X		switch(argv[1][1]) {
X
X		/* -a: add pairs of macros */
X		case 'a':
X			i = strlen(argv[1]) - 2;
X			if (i % 6 != 0)
X				usage();
X			/* look for empty macro slots */
X			for (i=0; br[i].opbr; i++)
X				;
X			for (cp=argv[1]+3; cp[-1]; cp += 6) {
X				br[i].opbr = malloc(3);
X				strncpy(br[i].opbr, cp, 2);
X				br[i].clbr = malloc(3);
X				strncpy(br[i].clbr, cp+3, 2);
X				addmac(br[i].opbr);	/* knows pairs are also known cmds */
X				addmac(br[i].clbr);
X				i++;
X			}
X			break;
X
X		/* -c: add known commands */
X		case 'c':
X			i = strlen(argv[1]) - 2;
X			if (i % 3 != 0)
X				usage();
X			for (cp=argv[1]+3; cp[-1]; cp += 3) {
X				if (cp[2] && cp[2] != '.')
X					usage();
X				strncpy(b1, cp, 2);
X				addmac(b1);
X			}
X			break;
X
X		/* -f: ignore font changes */
X		case 'f':
X			fflag = 1;
X			break;
X
X		/* -s: ignore size changes */
X		case 's':
X			sflag = 1;
X			break;
X		default:
X			usage();
X		}
X		argc--; argv++;
X	}
X
X	nfiles = argc - 1;
X
X	if (nfiles > 0) {
X		for (i=1; i<argc; i++) {
X			cfilename = argv[i];
X			f = fopen(cfilename, "r");
X			if (f == NULL)
X				perror(cfilename);
X			else
X				process(f);
X		}
X	} else {
X		cfilename = "stdin";
X		process(stdin);
X	}
X	exit(0);
X}
X
Xusage()
X{
X	printf("Usage: checknr -s -f -a.xx.yy.xx.yy... -c.xx.xx.xx...\n");
X	exit(1);
X}
X
Xprocess(f)
XFILE *f;
X{
X	register int i, n;
X	char mac[5];	/* The current macro or nroff command */
X	int pl;
X
X	stktop = -1;
X	for (lineno = 1; fgets(line, sizeof line, f); lineno++) {
X		if (line[0] == '.') {
X			/*
X			 * find and isolate the macro/command name.
X			 */
X			strncpy(mac, line+1, 4);
X			if (isspace(mac[0])) {
X				pe(lineno);
X				printf("Empty command\n");
X			} else if (isspace(mac[1])) {
X				mac[1] = 0;
X			} else if (isspace(mac[2])) {
X				mac[2] = 0;
X			} else if (mac[0] != '\\' || mac[1] != '\"') {
X				pe(lineno);
X				printf("Command too long\n");
X			}
X
X			/*
X			 * Is it a known command?
X			 */
X			checkknown(mac);
X
X			/*
X			 * Should we add it?
X			 */
X			if (eq(mac, "de"))
X				addcmd(line);
X
X			chkcmd(line, mac);
X		}
X
X		/*
X		 * At this point we process the line looking
X		 * for \s and \f.
X		 */
X		for (i=0; line[i]; i++)
X			if (line[i]=='\\' && (i==0 || line[i-1]!='\\')) {
X				if (!sflag && line[++i]=='s') {
X					pl = line[++i];
X					if (isdigit(pl)) {
X						n = pl - '0';
X						pl = ' ';
X					} else
X						n = 0;
X					while (isdigit(line[++i]))
X						n = 10 * n + line[i] - '0';
X					i--;
X					if (n == 0) {
X						if (stk[stktop].opno == SZ) {
X							stktop--;
X						} else {
X							pe(lineno);
X							printf("unmatched \\s0\n");
X						}
X					} else {
X						stk[++stktop].opno = SZ;
X						stk[stktop].pl = pl;
X						stk[stktop].parm = n;
X						stk[stktop].lno = lineno;
X					}
X				} else if (!fflag && line[i]=='f') {
X					n = line[++i];
X					if (n == 'P') {
X						if (stk[stktop].opno == FT) {
X							stktop--;
X						} else {
X							pe(lineno);
X							printf("unmatched \\fP\n");
X						}
X					} else {
X						stk[++stktop].opno = FT;
X						stk[stktop].pl = 1;
X						stk[stktop].parm = n;
X						stk[stktop].lno = lineno;
X					}
X				}
X			}
X	}
X	/*
X	 * We've hit the end and look at all this stuff that hasn't been
X	 * matched yet!  Complain, complain.
X	 */
X	for (i=stktop; i>=0; i--) {
X		complain(i);
X	}
X}
X
Xcomplain(i)
X{
X	pe(stk[i].lno);
X	printf("Unmatched ");
X	prop(i);
X	printf("\n");
X}
X
Xprop(i)
X{
X	if (stk[i].pl == 0)
X		printf(".%s", br[stk[i].opno].opbr);
X	else switch(stk[i].opno) {
X	case SZ:
X		printf("\\s%c%d", stk[i].pl, stk[i].parm);
X		break;
X	case FT:
X		printf("\\f%c", stk[i].parm);
X		break;
X	default:
X		printf("Bug: stk[%d].opno = %d = .%s, .%s",
X			i, stk[i].opno, br[stk[i].opno].opbr, br[stk[i].opno].clbr);
X	}
X}
X
Xchkcmd(line, mac)
Xchar *line;
Xchar *mac;
X{
X	register int i, n;
X
X	/*
X	 * Check to see if it matches top of stack.
X	 */
X	if (stktop >= 0 && eq(mac, br[stk[stktop].opno].clbr))
X		stktop--;	/* OK. Pop & forget */
X	else {
X		/* No. Maybe it's an opener */
X		for (i=0; br[i].opbr; i++) {
X			if (eq(mac, br[i].opbr)) {
X				/* Found. Push it. */
X				stktop++;
X				stk[stktop].opno = i;
X				stk[stktop].pl = 0;
X				stk[stktop].parm = 0;
X				stk[stktop].lno = lineno;
X				break;
X			}
X			/*
X			 * Maybe it's an unmatched closer.
X			 * NOTE: this depends on the fact
X			 * that none of the closers can be
X			 * openers too.
X			 */
X			if (eq(mac, br[i].clbr)) {
X				nomatch(mac);
X				break;
X			}
X		}
X	}
X}
X
Xnomatch(mac)
Xchar *mac;
X{
X	register int i, j;
X
X	/*
X	 * Look for a match further down on stack
X	 * If we find one, it suggests that the stuff in
X	 * between is supposed to match itself.
X	 */
X	for (j=stktop; j>=0; j--)
X		if (eq(mac,br[stk[j].opno].clbr)) {
X			/* Found.  Make a good diagnostic. */
X			if (j == stktop-2) {
X				/*
X				 * Check for special case \fx..\fR and don't
X				 * complain.
X				 */
X				if (stk[j+1].opno==FT && stk[j+1].parm!='R'
X				 && stk[j+2].opno==FT && stk[j+2].parm=='R') {
X					stktop = j -1;
X					return;
X				}
X				/*
X				 * We have two unmatched frobs.  Chances are
X				 * they were intended to match, so we mention
X				 * them together.
X				 */
X				pe(stk[j+1].lno);
X				prop(j+1);
X				printf(" does not match %d: ", stk[j+2].lno);
X				prop(j+2);
X				printf("\n");
X			} else for (i=j+1; i <= stktop; i++) {
X				complain(i);
X			}
X			stktop = j-1;
X			return;
X		}
X	/* Didn't find one.  Throw this away. */
X	pe(lineno);
X	printf("Unmatched .%s\n", mac);
X}
X
X/* eq: are two strings equal? */
Xeq(s1, s2)
Xchar *s1, *s2;
X{
X	return (strcmp(s1, s2) == 0);
X}
X
X/* print the first part of an error message, given the line number */
Xpe(lineno)
Xint lineno;
X{
X	if (nfiles > 1)
X		printf("%s: ", cfilename);
X	printf("%d: ", lineno);
X}
X
Xcheckknown(mac)
Xchar *mac;
X{
X
X	if (eq(mac, "."))
X		return;
X	if (binsrch(mac) >= 0)
X		return;
X	if (mac[0] == '\\' && mac[1] == '"')	/* comments */
X		return;
X
X	pe(lineno);
X	printf("Unknown command: .%s\n", mac);
X}
X
X/*
X * We have a .de xx line in "line".  Add xx to the list of known commands.
X */
Xaddcmd(line)
Xchar *line;
X{
X	char *mac;
X
X	/* grab the macro being defined */
X	mac = line+4;
X	while (isspace(*mac))
X		mac++;
X	if (*mac == 0) {
X		pe(lineno);
X		printf("illegal define: %s\n", line);
X		return;
X	}
X	mac[2] = 0;
X	if (isspace(mac[1]) || mac[1] == '\\')
X		mac[1] = 0;
X	if (ncmds >= MAXCMDS) {
X		printf("Only %d known commands allowed\n", MAXCMDS);
X		exit(1);
X	}
X	addmac(mac);
X}
X
X/*
X * Add mac to the list.  We should really have some kind of tree
X * structure here but this is a quick-and-dirty job and I just don't
X * have time to mess with it.  (I wonder if this will come back to haunt
X * me someday?)  Anyway, I claim that .de is fairly rare in user
X * nroff programs, and the register loop below is pretty fast.
X */
Xaddmac(mac)
Xchar *mac;
X{
X	register char **src, **dest, **loc;
X
X	if (binsrch(mac) >= 0){	/* it's OK to redefine something */
X#ifdef DEBUG
X		printf("binsrch(%s) -> already in table\n", mac);
X#endif DEBUG
X		return;
X	}
X	/* binsrch sets slot as a side effect */
X#ifdef DEBUG
Xprintf("binsrch(%s) -> %d\n", mac, slot);
X#endif
X	loc = &knowncmds[slot];
X	src = &knowncmds[ncmds-1];
X	dest = src+1;
X	while (dest > loc)
X		*dest-- = *src--;
X	*loc = malloc(3);
X	strcpy(*loc, mac);
X	ncmds++;
X#ifdef DEBUG
Xprintf("after: %s %s %s %s %s, %d cmds\n", knowncmds[slot-2], knowncmds[slot-1], knowncmds[slot], knowncmds[slot+1], knowncmds[slot+2], ncmds);
X#endif
X}
X
X/*
X * Do a binary search in knowncmds for mac.
X * If found, return the index.  If not, return -1.
X */
Xbinsrch(mac)
Xchar *mac;
X{
X	register char *p;	/* pointer to current cmd in list */
X	register int d;		/* difference if any */
X	register int mid;	/* mid point in binary search */
X	register int top, bot;	/* boundaries of bin search, inclusive */
X
X	top = ncmds-1;
X	bot = 0;
X	while (top >= bot) {
X		mid = (top+bot)/2;
X		p = knowncmds[mid];
X		d = p[0] - mac[0];
X		if (d == 0)
X			d = p[1] - mac[1];
X		if (d == 0)
X			return mid;
X		if (d < 0)
X			bot = mid + 1;
X		else
X			top = mid - 1;
X	}
X	slot = bot;	/* place it would have gone */
X	return -1;
X}
END-of-checknr.c
exit


exit 0 # Just in case...
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.