rsalz@uunet.uu.net (Rich Salz) (03/20/91)
Submitted-by: Kevin Braunsdorf <ksb@cc.purdue.edu> Posting-number: Volume 24, Issue 58 Archive-name: pucc-mk/part02 #!/bin/sh # This is part 02 of pucc-1c # ============= mk/mk.c ============== if test ! -d 'mk'; then echo 'x - creating directory mk' mkdir 'mk' fi if test -f 'mk/mk.c' -a X"$1" != X"-c"; then echo 'x - skipping mk/mk.c (File already exists)' else echo 'x - extracting mk/mk.c (Text)' sed 's/^X//' << 'Purdue' > 'mk/mk.c' && /* X * mk: detect and execute a compilation command X * (formerly called 'compile') X * X * example marker line: X * $Compile: cc -c -O %f X * $Compile (TEKECS): cc -c -DLOG -O %f X * $Compile (DEBUG): cc -c -g -DDEBUG %f X * $Compile=1: cc .... X * $Compile,cpu=100: .... X * X * marker lines can also be drawn from a standard template X * X * This program searches for the first occurence of a marker (DEFLTMARK) X * in the first block of the named file(s), grabs the line on which X * the marker occurs, performs some filename substitutions on the line, X * and prints the line (typically a shell command line) on the stdout. X * It can also set resource limits for valid(1L). X * X * this programs currently makes lots of substitutions (see the man page). X * X * command-line switches: X * see mk.m or main.c (or run mk -h) X * X * (c) Copyright 1983, Steven McGeady. All rights reserved. X * X * Written by S. McGeady, Intel, Inc., mcg@mipon2.intel.com (sm) X * Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb (ksb) X * X * This software is not subject to any license of the American Telephone X * and Telegraph Company or the Regents of the University of California. X * X * Permission is granted to anyone to use this software for any purpose on X * any computer system, and to alter it and redistribute it freely, subject X * to the following restrictions: X * X * 1. The authors are not held responsible for any consequences of the X * use of this software. X * X * 2. The origin of this software must not be misrepresented, either by X * explicit claim or by omission. Credit to the authors must appear X * in documentation and sources. X * X * 3. Altered versions must be plainly marked as such, and must not be X * misrepresented as being the original software. X * X * 4. This notice may not be removed or altered. X * X * All bug fixes and improvements should be mailed to the authors, X * if you can find them. X */ X #include <sys/param.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> #include <signal.h> #include <stdio.h> #include <ctype.h> X #include "machine.h" #include "getopt.h" #include "main.h" #include "mk.h" #include "rlimsys.h" X #if !defined(MAXPATHLEN) #define MAXPATHLEN 1024 #endif X #if RESOURCE #include <sys/resource.h> #include <sys/wait.h> #endif X #if defined(S_IFLNK) #define LSTAT lstat #else #define LSTAT stat #endif X extern char *strcpy(), *strrchr(), *strchr(); extern char *getenv(), *rcsname(); extern int errno; extern char *sys_errlist[]; #define strerror(Me) (sys_errlist[Me]) X char acOutMem[] = "%s: out of memory!\n"; static int marklen, sublen; static char *curfile, chFType; static char acSearch[MAXPATHLEN+1]; X #define MAXLINE BUFSIZ #define LEADCHAR '$' X #if defined(AUXDIR) static char acAuxDir[] = AUXDIR; #endif /* mk's default home */ X #if defined(BEFORE) static char acDefBefore[] = BEFORE; #endif /* default pre-scan */ X #if defined(TEMPL) static char acDefAfter[] = TEMPL; #endif /* default post-scan */ X /* X * option a good version description (ksb) X */ void Version() { X fprintf(stdout, "%s: %s\n", progname, "$Id: mk.c,v 4.6 90/11/19 14:06:30 ksb Exp $"); X fprintf(stdout, "%s: %%~ `%s\'\n", progname, acAuxDir); X fprintf(stdout, "%s: -e `%s\'\n", progname, X (char *)0 != pchExamine ? pchExamine : X #if defined(BEFORE) X acDefBefore #else X "" #endif /* default pre-scan */ X ); X fprintf(stdout, "%s: -t `%s\'\n", progname, X (char *)0 != pchTemplates ? pchTemplates : #if defined(TEMPL) X acDefAfter #else X "" #endif /* default post-scan */ X ); } X /* X * strip the case from an ident upto len or a char in pchStop (ksb) X */ void stripc(pchIdent, len, pchStop) char *pchIdent, *pchStop; int len; { X register int i; X X for (i = 0; i < len; ++i, ++pchIdent) { X if ((char *)0 != strchr(pchStop, *pchIdent)) X break; X if (isupper(*pchIdent)) X *pchIdent = tolower(*pchIdent); X } } X /* X * eat the submark portion (sm) X */ char * eatsub(pch, smark) char *pch; char *smark; { X while (isspace(*pch)) { X ++pch; X } X X if ((char *)0 != smark) { X if ('(' != *pch) { X return (char *)0; X } X X do X ++pch; X while (isspace(*pch)); X if (fCase) X stripc(pch, sublen, "):"); X if ('*' == smark[0] && '\000' == smark[1]) { X while (')' != pch[0] && '\000' != pch[0]) X ++pch; X } else if ('*' == pch[0]) { X ++pch; X } else if (0 != strncmp(pch, smark, sublen)) { X return (char *)0; X } else { X pch += sublen; X } X X while (isspace(*pch)) { X ++pch; X } X if (')' != *pch) { X return (char *)0; X } X do { X ++pch; X } while (isspace(*pch)); X } else if ('(' == *pch) { X while (')' != *pch) X ++pch; X ++pch; X while (isspace(*pch)) X ++pch; X } X return pch; } X /* X * find the end of the command string (ksb) X */ void cut(pch) char *pch; { X register char *q; X register int c; X X for (q = pch; '\000' != (c = *q); ++q) { X switch (c) { X case LEADCHAR: X if (LEADCHAR == q[1]) { X case '\n': X q[0] = '\000'; X return; X } X break; X case '\\': X if ('\000' != q[1]) X ++q; X break; X default: X break; X } X } } X /* X * copy chars with C escapes etc... (sm/ksb) X * since ANSI C won't let us write on constant strings we need fColon X * to stop on a colon durring template expansions X */ char * translit(dst, src, fColon) register char *dst; register char *src; int fColon; { X register char *tp; X register char *xp, *pcParam; X auto char sbPath[MAXPATHLEN+1]; X auto char acTemp[8]; /* buf for recursive calls */ X register int i; X register int num; X auto int fGrave; /* change %`name` into ${name} */ X #ifdef DEBUG X fprintf(stderr, "translit(%s)\n", src); #endif X X fGrave = 0; X pcParam = (char *)0; X while (*src) { X switch (*src) { X case '%': X switch (*++src) { X case 'A': X case 'a': X if (fAll || fFirst) { X if ('a' == *src) X *dst++ = '-'; X *dst++ = fFirst ? 'A' : 'a'; X } X break; X X case 'B': X strcpy(dst, progname); X dst += strlen(dst); X break; X X case 'b': X strcpy(dst, pathname); X dst += strlen(dst); X break; X X case 'C': X case 'c': /* mk command line flags */ X if (fConfirm) { X if ('c' == *src) X *dst++ = '-'; X *dst++ = 'c'; X } X break; X X case 'D': /* if not remote, fail */ X case 'd': /* directory part */ X if ((tp = strrchr(curfile, '/')) == NULL) { X if ('D' == *src) X if (debug) X fprintf(stderr, "%s: %s is not remote\n", progname, curfile); X return (char*)0; X break; X } X *tp = '\000'; X strcpy(dst, curfile); X dst += strlen(curfile); X *tp = '/'; X break; X X case 'e': X if ((char *)0 == pchExamine) X break; X *dst++ = '-'; X *dst++ = 'e'; X case 'E': X if ((char *)0 == pchExamine) X break; X strcpy(dst, pchExamine); X dst += strlen(dst); X break; X X case 'f': /* full filename */ X strcpy(dst, curfile); X dst += strlen(dst); X break; X X case 'F': /* file part only */ X if ((tp = strrchr(curfile, '/')) != NULL) { X tp++; X } else { X tp = curfile; X } X if ((xp = strrchr(tp, '.')) != NULL) { X *xp = '\000'; X strcpy(dst, tp); X *xp = '.'; X } else { X strcpy(dst, tp); X } X dst += strlen(dst); X break; X X case 'I': X case 'i': X if (fCase) { X if ('i' == *src) X *dst++ = '-'; X *dst++ = 'i'; X } X break; X X case 'l': X *dst++ = '-'; X *dst++ = 'l'; X case 'L': X sprintf(dst, "%d", lines); X dst += strlen(dst); X break; X X case 'm': /* marker we need */ X case 'M': /* lower case marker for make rules */ X if ('*' == markstr[0]) X *dst++ = '\\'; X strcpy(dst, markstr); X if ('M' == *src && !fCase) X stripc(dst, marklen, ""); X dst += marklen; X break; X X case 'N': X case 'n': X if (!fExec) { X if ('n' == *src) X *dst++ = '-'; X *dst++ = 'n'; X } X break; X X case 'o': X *dst++ = '-'; X case 'O': /* all switches, none can fail */ X (void)translit(dst, "%A%C%I%N%V", 0); X dst += strlen(dst); X break; X X case 'P': X case 'p': /* prefix */ X acTemp[0] = '%'; X acTemp[1] = *src + ('Q' - 'P'); X acTemp[2] = '.'; X acTemp[3] = '\000'; X if ((tp = translit(dst, acTemp, 0)) == NULL) { X return (char *)0; X } X dst += strlen(dst); X break; X X case 'Q': X case 'q': /* prefix-x, mostly internal */ X if ((tp = strrchr(curfile, '/')) != NULL) { X ++tp; X } else { X tp = curfile; X } X if ('\000' == *++src) X break; X if ((xp = strrchr(tp, *src)) != NULL) { X *xp = '\000'; X strcpy(dst, curfile); X *xp = *src; X } else { X if ('Q' == src[-1]) X return (char *)0; X strcpy(dst, curfile); X } X dst += strlen(dst); X break; X X case 'R': /* rcsbasename */ X if ((tp = strrchr(curfile, '/')) != NULL) { X tp++; X } else { X case 'r': /* rcsname */ X tp = curfile; X } X tp = rcsname(tp); X if ((char *)0 == tp) { X if (debug) X fprintf(stderr, "%s: no rcsfile for %s\n", progname, curfile); X return (char *)0; X } X strcpy(dst, tp); X dst += strlen(dst); X break; X X case 's': /* submarker we need */ X case 'S': /* lower case submarker for make rules */ X if ((char *)0 == submark) { X if (debug) X fprintf(stderr, "%s: no submarker for %s\n", progname, curfile); X return (char *)0; X } X if ('*' == submark[0]) X *dst++ = '\\'; X strcpy(dst, submark); X if ('S' == *src && !fCase) X stripc(dst, sublen, ""); X dst += sublen; X break; X X case 't': X if ((char *)0 == pchTemplates) X break; X *dst++ = '-'; X *dst++ = 't'; X case 'T': X if ((char *)0 == pchTemplates) X break; X strcpy(dst, pchTemplates); X dst += strlen(dst); X break; X X case 'U': X case 'u': /* extender-x, mostly internal */ X if ((tp = strrchr(curfile, '/')) != NULL) { X ++tp; X } else { X tp = curfile; X } X if ('\000' == *++src) X break; X if ((xp = strrchr(tp, *src)) != NULL) { X ++xp; X strcpy(dst, xp); X dst += strlen(dst); X } else if ('U' == src[-1]) { X return (char *)0; X } X break; X X case 'v': X *dst++ = '-'; X case 'V': X *dst++ = fVerbose ? 'v' : 's'; X if (debug) X *dst++ = 'V'; X break; X X case 'W': X case 'w': X if ((tp = strrchr(acSearch, '/')) == NULL) { X if ('W' == *src) X if (debug) X fprintf(stderr, "%s: %s is not remote\n", progname, acSearch); X return (char*)0; X break; X } X *tp = '\000'; X strcpy(dst, acSearch); X dst += strlen(dst); X *tp = '/'; X break; X X case 'X': X case 'x': /* extension */ X acTemp[0] = '%'; X acTemp[1] = *src + ('U' - 'X'); X acTemp[2] = '.'; X acTemp[3] = '\000'; X if ((tp = translit(dst, acTemp, 0)) == NULL) { X return (char *)0; X } X dst += strlen(dst); X break; X X case 'Y': X if ((*++src == '~') ? *++src == chFType : *src != chFType) { X if (debug) X fprintf(stderr, "%s: %s fails file type\n", progname, curfile); X return (char*)0; X } X break; X X case 'y': /* file type checks */ X *dst++ = chFType; X break; X X case 'Z': X case 'z': X if ((char *)0 == (tp = strrchr(acSearch, '/'))) { X tp = acSearch; X } else { X ++tp; X } X if ('\000' == *tp) { X if (debug) X fprintf(stderr, "%s: %s: not a template\n", progname, curfile); X return (char*)0; X } X strcpy(dst, tp); X dst += strlen(dst); X break; X X case '~': /* mk's home directory, so to speak */ X strcpy(dst, acAuxDir); X dst += strlen(dst); X break; X X case '\"': X case '`': X fGrave = 1; X /* FALLTHROUGH */ X case '{': /* } */ X *dst++ = '$'; X *dst++ = '{'; /* } */ X pcParam = dst; X break; X default: /* unrecognized chars are copied thru */ X *dst++ = *src; X break; X } X src++; X break; X X case '\\': X switch (*++src) { X case '\n': /* how would this happen? */ X ++src; X break; X case 'n': /* newline */ X *dst++ = '\n'; X ++src; X break; X case 't': X *dst++ = '\t'; X ++src; X break; X case 'b': X *dst++ = '\b'; X ++src; X break; X case 'r': X *dst++ = '\r'; X ++src; X break; X case 'f': X *dst++ = '\f'; X ++src; X break; X case 'v': X *dst++ = '\013'; X ++src; X break; X case '\\': X ++src; X case '\000': X *dst++ = '\\'; X break; X X case '0': case '1': case '2': case '3': X case '4': case '5': case '6': case '7': X num = *src++ - '0'; X for (i = 0; i < 2; i++) { X if (! isdigit(*src)) { X break; X } X num <<= 3; X num += *src++ - '0'; X } X *dst++ = num; X break; X case '8': case '9': X /* 8 & 9 are bogus octals, X * cc makes them literals X */ X /*fallthrough*/ X default: X *dst++ = *src++; X break; X } X break; X case '\"': X case '`': X if (fGrave) { X case /*{*/ '}': X *dst = '\000'; X if ((char *)0 != pcParam && (char *)0 == getenv(pcParam)) { X if (debug) X fprintf(stderr, "%s: %s: %s: not set\n", progname, curfile, pcParam); X return (char *)0; X } X pcParam = (char *)0; X *dst++ = /*{*/ '}'; X ++src, fGrave = 0; X break; X } X *dst++ = *src++; X break; X case ':': X if (fColon) { X src = ""; X break; X } X /*FALLTHROUGH*/ X default: X *dst++ = *src++; X break; X } X } X X *dst = '\000'; X return dst; } X #define EXIT_EXACT 0x00 /* an exact match */ #define EXIT_ANY 0x01 /* any value */ #define EXIT_NOT 0x02 /* not what was given */ typedef struct ECnode { X int ekey; /* key for a match */ X int ivalue; /* value to match */ } CODE; X /* X * Find a marker line in the given file, put it in a buffer and (sm/ksb) X * return a pointer to it. Limitted to count lines. X */ char * findmarker(fin, buf, pexitcode, count) register FILE *fin; char *buf; int count; CODE *pexitcode; { X register char *pch; X extern long atol(); X X if ((FILE *)0 == fin) X return (char *)0; X while ((char *)0 != (pch = fgets(buf, MAXLINE, fin))) { X if (--count < 0) { X pch = (char *)0; X if (debug) X fprintf(stderr, "%s: out of lines\n", progname); X break; X } X pch = strchr(buf, LEADCHAR); X if ((char *)0 == pch) X continue; X X do { X ++pch; X } while (isspace(*pch)); X X if (fCase) X stripc(pch, marklen, ":("/*)*/); X if ('*' == markstr[0] && '\000' == markstr[1]) { X while ('(' != pch[0] && ':' != pch[0] && '\000' != pch[0]) X ++pch; X } else if ('*' == pch[0]) { X ++pch; X } else if (0 != strncmp(markstr, pch, marklen)) { X continue; X } else { X pch += marklen; X } X X pch = eatsub(pch, submark); X if ((char *)0 == pch) { X continue; X } X X /* set exit code */ X pexitcode->ekey = EXIT_EXACT; X if ('=' == *pch) { X ++pch; X while (isspace(*pch)) X ++pch; X if ('~' == *pch) { /* ~code */ X pexitcode->ekey = EXIT_NOT; X ++pch; X } X while (isspace(*pch)) X ++pch; X if ('*' == *pch) { /* any code */ X pexitcode->ekey |= EXIT_ANY; X ++pch; X } else { X pexitcode->ivalue = atol(pch); X if ('-' == *pch || '+' == *pch) X ++pch; X while (isdigit(*pch)) X ++pch; X } X while (isspace(*pch)) X ++pch; X } else { X pexitcode->ivalue = 0; X } X #if RESOURCE X /* get resource limits */ X do { X if (',' == *pch) X ++pch; X stripc(pch, MAXLINE, ",:"); X pch = rparse(pch); X } while (',' == *pch); #else X /* try to eat resource limits */ X pch = strchr(pch, ':'); X if ((char *)0 == pch) X continue; #endif /* resource limits */ X X if (':' != *pch) X continue; X X /* found mk marker */ X do { X ++pch; X } while (isspace(*pch)); X cut(pch); X break; X } X return pch; } X /* X * place a variable in the environment (ksb) X */ int define(pch) char *pch; { X register char *p; X X p = strchr(pch, '='); X if ((char *)0 == p) { X fprintf(stderr, "%s: missing `=\' for define of %s\n", progname, pch); X return; X } X p[0] = '\000'; X setenv(pch, p+1); X p[0] = '='; } X /* X * remove a variable from the environment (ksb) X */ int undefine(pch) char *pch; { X setenv(pch, (char *)0); } X /* X * we have a filename and are ready to open and find a marker in it (sm/ksb) X */ int process(arg) char *arg; { X auto FILE *fin, *fpSrc; X auto char *pch, *pchSecond, *pchBefore, *pchLoop, *pchTrans; X auto int count, fFoundOne; X auto CODE exitcode; X auto char buf[MAXLINE]; X auto char combuf[MAXLINE]; X auto struct stat stIn; X #if RESOURCE X rinit(); X r_fTrace = debug; #endif /* resource limits */ X X curfile = arg; X if ('-' == curfile[0] && '\000' == curfile[1]) { X fprintf(stderr, "%s: stdin not supported\n", progname); X return 1; X } X if (-1 == LSTAT(curfile, & stIn)) { X fprintf(stderr, "%s: stat: %s: %s\n", progname, curfile, strerror(errno)); X return 1; X } X switch (stIn.st_mode & S_IFMT) { #if defined(S_IFLNK) X case S_IFLNK: /* symbolic link */ X chFType = 'l'; X break; #endif #if defined(S_IFIFO) X case S_IFIFO: /* fifo */ X chFType = 'p'; X break; #endif /* no fifos */ #if defined(S_IFSOCK) X case S_IFSOCK: /* socket */ X chFType = 's'; X break; #endif /* no sockets */ X case S_IFDIR: /* directory */ X chFType = 'd'; X break; X case S_IFCHR: /* character special */ X chFType = 'c'; X break; X case S_IFBLK: /* block special */ X chFType = 'b'; X break; X case 0: X case S_IFREG: /* regular */ X chFType = 'f'; X break; X default: X fprintf(stderr, "%s: stat: %s: unknown file type?\n", progname, curfile); X return 1; X } X if ('s' == chFType || 'd' == chFType) { X /* dirs and sockets don't have text to read */ X fpSrc = (FILE *)0; X } else if (NULL == (fpSrc = fopen(curfile, "r"))) { X fprintf(stderr, "%s: fopen: %s: %s\n", progname, curfile, strerror(errno)); X return 1; X } X X if ((char *)0 == markstr) { X if ('m' == progname[0] && 'k' == progname[1] && '\000' == progname[2]) X markstr = /*0m*/"Compile"; X else X markstr = progname; X } X while (isspace(*markstr)) { X ++markstr; X } X marklen = strlen(markstr); X if (fCase) X stripc(markstr, marklen, ""); X X if ((char *)0 != submark) { X while (isspace(*submark)) { X ++submark; X } X sublen = strlen(submark); X if (fCase) X stripc(submark, sublen, ""); X } else { X sublen = 0; X } X X pchBefore = pchExamine; #if defined(BEFORE) X if ((char *)0 == pchBefore) { X pchBefore = acDefBefore; X } #endif /* default pre-scan */ X pchSecond = pchTemplates; #if defined(TEMPL) X if ((char *)0 == pchSecond) { X pchSecond = acDefAfter; X } #endif /* default templates */ X X if (! fExec) X fVerbose = 1; X count = fFoundOne = 0; X fin = (FILE *)0; X do { X pch = findmarker(fin, & buf[0], & exitcode, lines); X if ((char *)0 == pch && (char *)0 != pchBefore) { X do { X pchLoop = strchr(pchBefore, ':'); X if ((char *)0 != pchLoop) X ++pchLoop; X pchTrans = translit(acSearch, pchBefore, 1); X pchBefore = pchLoop; X if ((FILE *)0 != fin) X fclose(fin); X fin = (char *)0 == pchTrans ? (FILE *)0 : fopen(acSearch, "r"); X } while ((FILE *)0 == fin && (char *)0 != pchBefore); X if (debug && (FILE *)0 != fin) { X fprintf(stderr, "%s: searching template %s\n", progname, acSearch); X } X if ((FILE *)0 != fin) X continue; X } X if ((char *)0 == pch && (FILE *)0 != fpSrc) { X if ((FILE*)0 != fin) X fclose(fin); X fin = fpSrc; X fpSrc = (FILE *)0; X acSearch[0] = '\000'; X if (debug) { X fprintf(stderr, "%s: searching file %s\n", progname, curfile); X } X continue; X } X if ((char *)0 == pch && (char *)0 != pchSecond) { X do { X pchLoop = strchr(pchSecond, ':'); X if ((char *)0 != pchLoop) X ++pchLoop; X pchTrans = translit(acSearch, pchSecond, 1); X pchSecond = pchLoop; X if ((FILE *)0 != fin) X fclose(fin); X fin = (char *)0 == pchTrans ? (FILE *)0 : fopen(acSearch, "r"); X } while ((FILE *)0 == fin && (char *)0 != pchSecond); X if (debug && (FILE *)0 != fin) { X fprintf(stderr, "%s: searching template %s\n", progname, acSearch); X } X continue; X } X if ((char *)0 == pch) { X break; X } X if ((char *)0 == translit(combuf, pch, 0)) { X continue; X } X fFoundOne = 1; X if (fConfirm) { X fprintf(stderr, "\t%s [fnyq]? ", combuf); X fflush(stderr); X gets(buf); X X for (pch = buf; '\000' != *pch && isspace(*pch); ++pch) X /* empty */; X switch (*pch) { X case 'q': X case 'Q': X exit(0); X case 'y': X case 'Y': X break; X case 'f': X case 'F': X default: X continue; X case 'n': X case 'N': X if ((FILE *)0 != fin) X fclose(fin); X return 0; X } X } else if (fVerbose) { X fprintf(stderr, "\t%s\n", combuf); X } X fflush(stderr); X if (fExec) { X auto int cur, code; #if RESOURCE X code = rlimsys(combuf); #else /* use vanilla system */ X code = system(combuf); X if (0177 == (code & 0xff)) { /* stopped */ X code = (code >> 8) & 0xff; X } else if (0 != (code & 0xff)) { /* killed */ X code = code & 0x7f; X } else { /* exit */ X code = (code >> 8) & 0xff; X } #endif /* check for resource limits */ X if (debug) X fprintf(stderr, "%s: command exits %d\n", progname, code); X switch (exitcode.ekey) { X case EXIT_EXACT: X cur = code != exitcode.ivalue; X break; X case EXIT_EXACT|EXIT_NOT: X cur = code == exitcode.ivalue; X break; X case EXIT_ANY: X cur = 0; X break; X case EXIT_ANY|EXIT_NOT: X cur = 1; X break; X } X if (fFirst && 0 == cur) { X if ((FILE *)0 != fin) X fclose(fin); X return 0; X } X count += cur; X } X if (fAll || fFirst) { X continue; X } X if ((FILE *)0 != fin) { X fclose(fin); X } X return count; X } while ((FILE *)0 != fin); X if (fVerbose && !fFoundOne) { X fprintf(stderr, "%s: no marker \"%c%s", progname, LEADCHAR, markstr); X if ((char *)0 != submark) { X fprintf(stderr, "(%s)", submark); X } X fprintf(stderr, ":\" %s %s\n", fFoundOne ? "selected from" : "in", curfile); X } X if (!fFoundOne) X ++count; X return count; } Purdue chmod 0444 mk/mk.c || echo 'restore of mk/mk.c failed' Wc_c="`wc -c < 'mk/mk.c'`" test 21421 -eq "$Wc_c" || echo 'mk/mk.c: original size 21421, current size' "$Wc_c" fi # ============= mk/Makefile ============== if test -f 'mk/Makefile' -a X"$1" != X"-c"; then echo 'x - skipping mk/Makefile (File already exists)' else echo 'x - extracting mk/Makefile (Text)' sed 's/^X//' << 'Purdue' > 'mk/Makefile' && # Written by S. McGeady, Intel, Inc., mcg@mipon2.intel.com (sm) # Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb (ksb) # # This software is not subject to any license of the American Telephone # and Telegraph Company or the Regents of the University of California. # # Permission is granted to anyone to use this software for any purpose on # any computer system, and to alter it and redistribute it freely, subject # to the following restrictions: # # 1. The authors are not held responsible for any consequences of the # use of this software. # # 2. The origin of this software must not be misrepresented, either by # explicit claim or by omission. Credit to the authors must appear # in documentation and sources. # # 3. Altered versions must be plainly marked as such, and must not be # misrepresented as being the original software. # # 4. This notice may not be removed or altered. # # Makefile for mk # # $Id: Makefile.plain,v 4.6 90/11/19 13:57:12 ksb Exp $ X PROG= mk BIN= ${DESTDIR}/usr/local/bin X L=../libopt #L=/usr/include/local I=/usr/include S=/usr/include/sys P= X INCLUDE= -I$L DEBUG= -O CDEFS= CFLAGS= ${DEBUG} ${CDEFS} ${INCLUDE} X HDR= main.h mk.h optaccum.h machine.h rlimsys.h SRC= main.c mk.c rcsname.c setenv.c optaccum.c rlimsys.c GENc= GENh= GEN= ${GENh} ${GENc} DEP= ${SRC} ${GENc} OBJ= main.o mk.o rcsname.o setenv.o optaccum.o rlimsys.o MAN= mk.1l SOURCE= Makefile Makefile.mkcmd README mk.m ${MAN} ${HDR} ${SRC} X all: ${PROG} X ${PROG}:$P ${OBJ} # ${CC} -o $@ ${CFLAGS} ${OBJ} -lopt # ${CC} -o $@ ${CFLAGS} ${OBJ} -L /usr/local/lib -lopt X ${CC} -o $@ ${CFLAGS} ${OBJ} ../libopt/libopt.a X self-test: ${PROG} X cd Tests; ../${PROG} -mCompile * X swap: ${HDR} ${SRC} ${GEN} Makefile.mkcmd X mkcmd std_help.m mk.m X -(cmp -s prog.c main.c || echo main.c changed) X -(cmp -s prog.h main.h || echo main.h changed) X mv Makefile Makefile.plain X mv Makefile.mkcmd Makefile X clean: FRC X rm -f Makefile.bak *.o prog.[ch] ${GEN} ${PROG} a.out core errs tags X depend: ${HDR} ${SRC} ${GEN} FRC X maketd ${CDEFS} ${INCLUDE} ${DEP} X install: all FRC X install -cs ${PROG} ${BIN}/${PROG} X lint: ${HDR} ${SRC} ${GEN} FRC X lint -h ${CDEFS} ${INCLUDE} ${DEP} X mkcat: ${MAN} X mkcat ${MAN} X print: source FRC X lpr -J'${PROG} source' ${SOURCE} X source: ${SOURCE} X spotless: clean X rcsclean ${SOURCE} X tags: ${HDR} ${SRC} ${GEN} X ctags -t ${HDR} ${SRC} ${GEN} X ${SOURCE}: X co -q $@ X FRC: X # DO NOT DELETE THIS LINE - maketd DEPENDS ON IT X main.o: machine.h main.c optaccum.h X mk.o: machine.h main.h mk.c mk.h rlimsys.h X rcsname.o: machine.h rcsname.c X setenv.o: machine.h setenv.c X optaccum.o: machine.h optaccum.c X rlimsys.o: machine.h mk.h rlimsys.c rlimsys.h X # *** Do not add anything here - It will go away. *** Purdue chmod 0644 mk/Makefile || echo 'restore of mk/Makefile failed' Wc_c="`wc -c < 'mk/Makefile'`" test 2737 -eq "$Wc_c" || echo 'mk/Makefile: original size 2737, current size' "$Wc_c" fi # ============= mk/Tests/ExitCodes ============== if test ! -d 'mk/Tests'; then echo 'x - creating directory mk/Tests' mkdir 'mk/Tests' fi if test -f 'mk/Tests/ExitCodes' -a X"$1" != X"-c"; then echo 'x - skipping mk/Tests/ExitCodes (File already exists)' else echo 'x - extracting mk/Tests/ExitCodes (Text)' sed 's/^X//' << 'Purdue' > 'mk/Tests/ExitCodes' && # This file checks mk's exit code traps (ksb) # $Id$ X mk -s $0 exit $? X $Compile:\nfor group in True False Any None Number Sig;\ndo\n\t%b %v -m$group %f || (echo failed $group );\ndone\necho OK X X These tests check for the different ways to say `zero is a good value' X $True=0: \n%b -a%V -t/dev/null -mOK %f $OK(1) : : $OK(2)=0 : : $OK(3)=1 : exit 1 $OK(4)=~0: exit 1 $OK(5)=~1: : $OK(6) : : $OK(7)=0 : : $OK(8)=~1: : X These thests check for the different fail under `zero is a good value' $False=7: \n%b -a%V -t/dev/null -mBAD %f $BAD(1) : exit 1 $BAD(2)=0 : exit 1 $BAD(3)=1 : : $BAD(4)=~0: : $BAD(5)=~1: exit 1 $BAD(6)=1 : exit 0 $BAD(7)=~0: exit 0 X These tests must all always succeed $Any=0: \n%b -a%V -t/dev/null -mANY %f $ANY(1)=*: : $ANY(2)=*: exit 0 $ANY(3)=*: exit 1 $ANY(4)=*: exit 5 X These tests should all always fail $None=4: \n%b -a%V -t/dev/null -mNONE %f $NONE(1)=~*: : $NONE(2)=~*: exit 0 $NONE(3)=~*: exit 1 $NONE(4)=~*: exit 5 X $Number=0: \n%b -a%V -t/dev/null -mVALUE %f $VALUE(0)=0: exit 0 $VALUE(1)=1: exit 1 $VALUE(2)=2: exit 2 $VALUE(3)=3: exit 3 $VALUE(4)=4: exit 4 X $Sig=0: \n%b -a%V -t/dev/null -mSIG %f $SIG=1: kill -1 \$$ $SIG=15: kill -15 \$$ Purdue chmod 0644 mk/Tests/ExitCodes || echo 'restore of mk/Tests/ExitCodes failed' Wc_c="`wc -c < 'mk/Tests/ExitCodes'`" test 1182 -eq "$Wc_c" || echo 'mk/Tests/ExitCodes: original size 1182, current size' "$Wc_c" fi # ============= mk/Tests/compat ============== if test -f 'mk/Tests/compat' -a X"$1" != X"-c"; then echo 'x - skipping mk/Tests/compat (File already exists)' else echo 'x - extracting mk/Tests/compat (Text)' sed 's/^X//' << 'Purdue' > 'mk/Tests/compat' && $Compile: MK=-t/dev/null %b -a%C%I%N%V -mVars -Dfalse="exit 0" %f && echo Vars test OK X # the OLD mk used to expand shell variable and some other strange # command line variables. These have been passed off to the shell # to do. This test makes sure OLD style calls get converted into # shell variable substitutions correctly. (ksb) X $Vars(1): %`false` $Vars(2): %{false} Purdue chmod 0644 mk/Tests/compat || echo 'restore of mk/Tests/compat failed' Wc_c="`wc -c < 'mk/Tests/compat'`" test 376 -eq "$Wc_c" || echo 'mk/Tests/compat: original size 376, current size' "$Wc_c" fi # ============= mk/rlimsys.h ============== if test -f 'mk/rlimsys.h' -a X"$1" != X"-c"; then echo 'x - skipping mk/rlimsys.h (File already exists)' else echo 'x - extracting mk/rlimsys.h (Text)' sed 's/^X//' << 'Purdue' > 'mk/rlimsys.h' && /* X * include file for rlimited system X */ X #if RESOURCE extern int rlimsys(); extern char *rparse(); extern void rinit(); extern int r_fTrace; /* use shell -cx for verbose trace */ #endif Purdue chmod 0444 mk/rlimsys.h || echo 'restore of mk/rlimsys.h failed' Wc_c="`wc -c < 'mk/rlimsys.h'`" test 190 -eq "$Wc_c" || echo 'mk/rlimsys.h: original size 190, current size' "$Wc_c" fi # ============= mkcat/scan.c ============== if test ! -d 'mkcat'; then echo 'x - creating directory mkcat' mkdir 'mkcat' fi if test -f 'mkcat/scan.c' -a X"$1" != X"-c"; then echo 'x - skipping mkcat/scan.c (File already exists)' else echo 'x - extracting mkcat/scan.c (Text)' sed 's/^X//' << 'Purdue' > 'mkcat/scan.c' && /* X * Copyright 1990 Purdue Research Foundation, West Lafayette, Indiana X * 47907. All rights reserved. X * X * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb X * X * This software is not subject to any license of the American Telephone X * and Telegraph Company or the Regents of the University of California. X * X * Permission is granted to anyone to use this software for any purpose on X * any computer system, and to alter it and redistribute it freely, subject X * to the following restrictions: X * X * 1. Neither the authors nor Purdue University are responsible for any X * consequences of the use of this software. X * X * 2. The origin of this software must not be misrepresented, either by X * explicit claim or by omission. Credit to the authors and Purdue X * University must appear in documentation and sources. X * X * 3. Altered versions must be plainly marked as such, and must not be X * misrepresented as being the original software. X * X * 4. This notice may not be removed or altered. X */ X /* $Id: scan.c,v 3.7 90/11/28 09:54:33 ksb Exp $ X * X * scan -- look for trouble in the cat dirs (mostly) (ksb) X */ #include "machine.h" X #include <stdio.h> #include <ctype.h> #include <math.h> #include <errno.h> #include <sys/file.h> #include <sys/types.h> #include <sys/param.h> #if defined(SYSV) #include <ndir.h> #else #include <sys/dir.h> #endif #include <sys/stat.h> #include <fcntl.h> X #if !defined(MAXPATHLEN) #define MAXPATHLEN 1024 #endif X #include "main.h" #include "pt.h" #include "genwhatis.h" #include "format.h" #include "readman.h" #include "mkcat.h" X extern FILE *popen(); extern long atol(); extern int errno; extern char *sys_errlist[]; #define strerror(Me) sys_errlist[Me] X extern char *strrchr(), *strcat(), *strcpy(), *malloc(), *realloc(); X #if !defined(F_OK) #define F_OK 0 #endif X #if !defined(R_OK) #define R_OK 4 #endif X X /* X * are these two files the same? (ksb) X * same inode X * return yes X * same compress status? X * return system("cmp -s file1 file2"); X * diff compress status X * return system("zcat file.Z | cmp -s - file"); X */ int CmpFile(pcFile1, pcFile2) char *pcFile1, *pcFile2; { X register char *pcComp1, *pcComp2; X auto struct stat stat1, stat2; X auto char acCmd[MAXPATHLEN*4]; X X if (-1 == stat(pcFile1, & stat1) || -1 == stat(pcFile2, & stat2)) { X return 0; X } X if (stat1.st_ino == stat2.st_ino && stat1.st_dev == stat2.st_dev) { X return 1; X } X pcComp1 = strrchr(pcFile1, acDotZ[0]); X if ((char *)0 != pcComp1 && 0 != strcmp(acDotZ, pcComp1)) { X pcComp1 = (char *)0; X } X pcComp2 = strrchr(pcFile2, acDotZ[0]); X if ((char *)0 != pcComp2 && 0 != strcmp(acDotZ, pcComp2)) { X pcComp2 = (char *)0; X } X if (((char *)0 != pcComp1) == ((char *)0 != pcComp2)) { X sprintf(acCmd, "exec cmp -s \'%s\' \'%s\'", pcFile1, pcFile2); X } else if ((char *)0 == pcComp2) { X sprintf(acCmd, "zcat \'%s\' |exec cmp -s - \'%s\'", pcFile1, pcFile2); X } else { X sprintf(acCmd, "zcat \'%s\' |exec cmp -s - \'%s\'", pcFile2, pcFile1); X } X return 0 == system(acCmd); } X /* X * if we find a compress'd file uncompress if (fCompress) (ksb) X * if we find an uncompress'd file coompress if (!fCompress) X * make symboliclinks point to correct version. X * X * piNeed is a count of how many we would change X */ static int FixCompress(npg, ppDE, piNeed) int npg, *piNeed; struct direct **ppDE; { X auto char acOther[MAXPATHLEN+1]; X auto char acCmd[MAXPATHLEN*2+100]; X auto char acLink[MAXPATHLEN+1]; X struct direct *pDEThis; X register int i, j; X auto int iLinkLen; X auto PATH PTThis, PTLink; X auto struct stat stThis; X X *piNeed = 0; X for (i = 0; i < npg; ++i) { X if ((struct direct *)0 == (pDEThis = ppDE[i])) X continue; X ppDE[i] = (struct direct *)0; X PTInit(&PTThis, pDEThis->d_name); X if (fCompress) { X if (PTIsComp(&PTThis)) X continue; X (void)strcpy(acOther, PTFull(&PTThis)); X (void)PTComp(&PTThis); X } else { X if (!PTIsComp(&PTThis)) X continue; X (void)strcpy(acOther, PTFull(&PTThis)); X (void)PTUnComp(&PTThis); X } X X /* now Other has the wrong one, and PTThis is correct X */ X if (-1 == LSTAT(acOther, & stThis)) { X /* someone removed it, ouch */ X continue; X } X if (0 == access(PTFull(&PTThis), F_OK)) { X if (0 == CmpFile(PTFull(&PTThis), acOther)) { X if (fVerbose) { X (void)printf("%s: rm -f %s\n", progname, acOther); X } X if (fExec && -1 == unlink(acOther)) { X fprintf(stderr, "%s: unlink: %s: %s\n", progname, acOther, strerror(errno)); X exit(22); X } X } else { X (void)printf("%s: %s ~ %s, both exist; delete one and retry\n", progname, PTFull(&PTThis), acOther); X exit(88); X } X } X switch (stThis.st_mode & S_IFMT) { X case S_IFREG: X (void)sprintf(acCmd, "%scompress -f -c \"%s\" >\"%s\"", fCompress ? "" : "un", acOther, PTFull(&PTThis)); X if (fVerbose) X printf("%s: %s\n", progname, acCmd); X if (fExec && 0 != system(acCmd)) X continue; X ++*piNeed; X for (j = i+1; j < npg; ++j) { X if ((struct direct *)0 == ppDE[j]) X continue; X if (ppDE[j]->d_ino != pDEThis->d_ino) X continue; X PTInit(&PTLink, ppDE[j]->d_name); X if (fVerbose) X printf("%s: rm -f %s\n", progname, PTFull(&PTLink)); X if (fExec) X (void)unlink(PTFull(&PTLink)); X if (fCompress) X (void)PTComp(&PTLink); X else X (void)PTUnComp(&PTLink); X if (fVerbose) X printf("%s: ln %s %s\n", progname, PTFull(&PTThis), PTFull(&PTLink)); X if (fExec && 0 != link(PTFull(&PTThis), PTFull(&PTLink))) { X fprintf(stderr, "%s: link: %s: %s\n", progname, PTFull(&PTLink), strerror(errno)); X } X ppDE[j] = (struct direct *)0; X } X if (fVerbose) { X printf("%s: rm -f %s\n", progname, acOther); X } X if (fExec && 0 != unlink(acOther)) { X fprintf(stderr, "%s: unlink: %s: %s\n", progname, acOther, strerror(errno)); X } X break; #if HAVE_SLINKS X case S_IFLNK: X if (-1 == (iLinkLen = readlink(acOther, acLink, MAXPATHLEN))) { X fprintf(stderr, "%s: readlink: %s: %s\n", progname, acOther, strerror(errno)); X continue; X } X acLink[iLinkLen] = '\000'; X printf("%s: %s -> %s\n", progname, acOther, acLink); X if ('/' == acLink[0]) X continue; X PTInit(& PTLink, acLink); X if (fCompress) X (void)PTComp(&PTLink); X else X (void)PTUnComp(&PTLink); X if (fVerbose) X (void)printf("%s: rm -f \"%s\"\n", progname, acOther); X if (fExec && 0 != unlink(acOther)) X fprintf(stderr, "%s: unlink: %s: %s\n", progname, acOther, strerror(errno)); X if (fVerbose) X (void)printf("%s: ln -s \"%s\" \"%s\"\n", progname, PTFull(&PTLink), PTFull(&PTThis)); X if (fExec && symlink(PTFull(&PTThis), PTFull(&PTLink))) X fprintf(stderr, "%s: link: %s\n", progname, strerror(errno));; X break; #endif X case S_IFDIR: X /* OLD, for instance */ X continue; X default: X (void)fprintf(stderr, "%s: %s: unknown file type\n", progname, PTFull(&PTThis)); X continue; X } X } X return 0; } X /* X * In this alphasorted list of (struct direct *) [some nil] find (ksb) X * (via a binary search) the named file... return a pointer to X * the entry so we may remove it in the caller X * X * This allows the caller to `shoot' the return array from scandir X * a few files at a time and still have a list of the files left. X */ static struct direct ** FindDEnt(pcName, ppDE, n) char *pcName; struct direct **ppDE; int n; { X register int i, mid; X register int cmp; X X if (0 == n) X return (struct direct **)0; X for (i = 0; i < n; ++i) { X if ((struct direct *)0 != ppDE[i]) X break; X } X if (n == i) { X return (struct direct **)0; X } X X if (0 == strcmp(pcName, (ppDE[i])->d_name)) { X return & ppDE[i]; X } X X do { X --n; X if ((struct direct *)0 != ppDE[n]) X break; X } while (n > i); X if (0 == strcmp(pcName, (ppDE[n])->d_name)) { X return & ppDE[n]; X } X X while (i != n) { X mid = (n+i)/2; X while (mid < n && (struct direct *)0 == ppDE[mid]) X ++mid; X if (mid == n) { X mid = (n+i)/2; X while (mid > i && (struct direct *)0 == ppDE[mid]) X --mid; X if (mid == i) X break; X } X cmp = strcmp(pcName, (ppDE[mid])->d_name); X if (cmp == 0) { X return & ppDE[mid]; X } X if (cmp < 0) { X n = mid; X } else { X i = mid; X } X if (i+1 == n) X break; X } X return (struct direct **)0; } X /* X * remove a link entry from something (ksb) X * 1 == OK to build X * 0 == done for us already X * -1 == error on rm X * 2 == just struke link from list, fMkLinks is not set! X */ static int FixLink(pcEnt, ppDEPages, npg, pcDest, pstCat) char *pcEnt, *pcDest; /* ZZZ where the link points */ struct direct **ppDEPages; int npg; struct stat *pstCat; { X register struct direct **ppDE; X int iLinkLen; X auto char acContents[MAXPATHLEN+1]; X auto struct stat stDest; X X if ((struct direct **)0 != ppDEPages && (struct direct **)0 != (ppDE = FindDEnt(pcEnt, ppDEPages, npg))) { X *ppDE = (struct direct *)0; X } X if (!fMkLinks) X return 2; X X if (-1 == LSTAT(pcEnt, & stDest)) { X if (ENOENT != errno) { X fprintf(stderr, "%s: stat: %s: %s\n", progname, pcEnt, strerror(errno)); X return -1; X } X return 1; X } X switch (stDest.st_mode & S_IFMT) { X case 0: X case S_IFREG: /* regular */ X if (fUseHards) { X if ((struct stat *)0 != pstCat && stDest.st_ino == pstCat->st_ino && stDest.st_dev == pstCat->st_dev) { X /* OK */ X return 0; X } X if (stDest.st_nlink < 2) { X fprintf(fpOut, "%s: `%s\' is a unique file, not removed\n", progname, pcEnt); X return -1; X } X fprintf(fpOut, "%s: hard moved: %s@ -> %s\n", progname, pcEnt, pcDest); X } else { X /* if this is not the old cat page, and X * a unique file that is not a clone of the page X * remove it. X */ X if ((struct stat *)0 != pstCat && (stDest.st_ino == pstCat->st_ino && stDest.st_dev == pstCat->st_dev)) { X /* it is an OK hard link, dink it */; X } else if (stDest.st_nlink < 2 && 0 != CmpFile(pcEnt, acCat)) { X fprintf(fpOut, "%s: `%s\' is a unique file, not removed\n", progname, pcEnt); X return -1; X } X fprintf(fpOut, "%s: hard link changed to symbolic link: %s@ -> %s\n", progname, pcEnt, pcDest); X } X break; X #if HAVE_SLINKS X case S_IFLNK: /* symbolic link */ X if (-1 == (iLinkLen = readlink(pcEnt, acContents, MAXPATHLEN))) { X fprintf(stderr, "%s: readlink: %s: %s\n", progname, pcEnt, strerror(errno)); X return -1; X } X acContents[iLinkLen] = '\000'; X if (fUseHards) { X if (0 != strcmp(acContents, pcDest) && 0 != CmpFile(pcEnt, acContents)) { X return -1; X } X fprintf(fpOut, "%s: symbolic link changed to a hard link: %s -> %s\n", progname, pcEnt, pcDest); X } else { X if (0 == strcmp(acContents, pcDest)) { X return 0; X } X fprintf(fpOut, "%s: symbolic link moved: %s@ -> %s\n", progname, pcEnt, pcDest); X } X break; X default: X fprintf(fpOut, "%s: `%s\' is a special file, cannot change to a symlink\n", progname, pcEnt); X return -1; X #else X default: X fprintf(fpOut, "%s: `%s\' is a special file, cannot change to a link\n", progname, pcEnt); X return -1; #endif X } X /* link was ok to remove and pointed the wrong place X */ X if (fVerbose) { X fprintf(fpOut, "%s: rm -f \"%s\"\n", progname, pcEnt); X } X if (fExec) { X (void)unlink(pcEnt); X } X return 1; } X /* X * update all the links that point to this page (ksb) X * X * We chdir() to the target dir and chdir back when done. X * X * Passed a list of links to make, a file to link them to, X * the stat buffer for an `OK' previous file that links may X * have been hard linked to, and a list of files in that directory X * that we should `punch out' as they are claimed as links X * (so we don't have to do this again). X * X * Optionally, remove the links... X */ static void MkLinks(pcBase, pWU, ppDEPages, npg, pstCat, fRemove) WHATIS *pWU; char *pcBase; struct direct **ppDEPages; int npg, fRemove; struct stat *pstCat; { X extern char *getwd(); X register int k; X auto char acCLink[MAXPATHLEN+1]; X X for (k = 0; k < pWU->ilen; ++k) { X /* do not link file to itself */ X (void)sprintf(acCLink, "%s.%s%s", pWU->ppclist[k], pWU->pcext, fCompress ? acDotZ : ""); X if (0 == strcmp(pcBase, acCLink)) { X continue; X } X X switch (FixLink(acCLink, ppDEPages, npg, pcBase, pstCat)) { X case 1: /* make it -- it is not there */ X if (fRemove) { X continue; X } X /* make it below */ X break; X X case 0: /* it is there already */ X if (!fRemove) { X continue; X } X if (fVerbose) { X fprintf(fpOut, "%s: rm -f \"%s\"\n", progname, acCLink); X } X if (fExec) { X (void)unlink(acCLink); X } X continue; X X case -1: /* error on remove/check of link -- forget it */ X continue; X case 2: /* fMkLinks is 0 */ X continue; X } X X /* make a link X */ X if (fUseHards) { X if (fVerbose) { X fprintf(fpOut, "%s: ln \"%s\" \"%s\"\n", progname, pcBase, acCLink); X } X if (fExec && 0 != link(pcBase, acCLink)) { X fprintf(stderr, "%s: link: %s to %s: %s\n", progname, pcBase, acCLink, strerror(errno)); X continue; X } X } else { X if (fVerbose) { X fprintf(fpOut, "%s: ln -s \"%s\" \"%s\"\n", progname, pcBase, acCLink); X } X if (fExec && 0 != symlink(pcBase, acCLink)) { X fprintf(stderr, "%s: symlink: %s to %s: %s\n", progname, pcBase, acCLink, strerror(errno)); X continue; X } X } X } } X /* X * select routine for scandir (ksb) X */ static int catSelect(pEnt) struct direct *pEnt; { X if ('.' == pEnt->d_name[0] && ('\000' == pEnt->d_name[1] || X ('.' == pEnt->d_name[1] && '\000' == pEnt->d_name[2]))) { X return 0; X } X return 1; } X /* X * scan the read directory for pages that differ only (ksb) X * in extender or case X */ static void ScanDups(npg, ppDEPages, pWU) int npg; register struct direct **ppDEPages; register WHATIS *pWU; { X register int k, l; X register int ck, cl; X register char *pcCompk, *pcCompl; X register char *pcSectionk, *pcSectionl; X X for (k = 0; k < npg; ++k) { X pWU[k].isection = 1; X } X X pcSectionk = pcCompk = (char *)0; X pcSectionl = pcCompl = (char *)0; X for (k = 0; k < npg; X ((char *)0 != pcCompk && (*pcCompk = acDotZ[0])), X ((char *)0 != pcSectionk && (*pcSectionk = '.')), X (pWU[k++].isection = 0)) { X if ((struct direct *)0 == ppDEPages[k]) { X continue; X } X pcCompk = strrchr(ppDEPages[k]->d_name, acDotZ[0]); X if ((char *)0 != pcCompk && 0 != strcmp(pcCompk, acDotZ)) { X pcCompk = (char *)0; X } X if ((char *)0 != pcCompk) { X *pcCompk = '\000'; X } X pcSectionk = strrchr(ppDEPages[k]->d_name, '.'); X if ((char *)0 == pcSectionk) { X continue; X } X *pcSectionk = '\000'; X if (IsOKBase(ppDEPages[k]->d_name)) { X continue; X } X ck = ppDEPages[k]->d_name[0]; X if (isalpha(ck) && isupper(ck)) { X ck = tolower(ck); X } X X for (l = k+1; l < npg; X ((char *)0 != pcCompl && (*pcCompl = acDotZ[0])), X ((char *)0 != pcSectionl && (*pcSectionl = '.')), X ++l) { X if ((struct direct *)0 == ppDEPages[l] || 0 == pWU[l].isection) X continue; X cl = ppDEPages[l]->d_name[0]; X if (isalpha(cl) && isupper(cl)) X cl = tolower(cl); X if (cl != ck) { X continue; X } X pcCompl = strrchr(ppDEPages[l]->d_name, acDotZ[0]); X if ((char *)0 != pcCompl && 0 != strcmp(pcCompl, acDotZ)) { X pcCompl = (char *)0; X } X if ((char *)0 != pcCompl) { X *pcCompl = '\000'; X } X pcSectionl = strrchr(ppDEPages[l]->d_name, '.'); X if ((char *)0 == pcSectionl) { X continue; X } X *pcSectionl = '\000'; X if (0 != strcasecmp(ppDEPages[k]->d_name, ppDEPages[l]->d_name)) { X continue; X } X fprintf(fpOut, "%s: `%s.%s%s\' and `%s.%s%s\' differ ", progname, X ppDEPages[k]->d_name, pcSectionk+1, (char *)0 != pcCompk ? acDotZ : "", X ppDEPages[l]->d_name, pcSectionl+1, (char *)0 != pcCompl ? acDotZ : ""); X pWU[l].isection = 0; X if (0 == strcmp(ppDEPages[k]->d_name, ppDEPages[l]->d_name) && 0 == strcmp(pcSectionk+1, pcSectionl+1)) { X fprintf(fpOut, "only in compression\n"); X } else if (0 == strcasecmp(pcSectionk+1, pcSectionl+1)) { X if (((char *)0 == pcCompl) == ((char *)0 == pcCompk)) { X fprintf(fpOut, "only in case\n"); X } else { X fprintf(fpOut, "in case and compression\n"); X } X } else if (((char *)0 == pcCompl) == ((char *)0 == pcCompk)) { X fprintf(fpOut, "only in section\n"); X } else { X fprintf(fpOut, "in section and compression\n"); X } X } X } } X /* remove the case from a file name, but don't touch the acDotZ if (ksb) X * it has one X */ void DropCase(pc, fComp) char *pc; int fComp; { X for (/* done*/; '\000' != *pc; ++pc) { X if (fComp && *pc == acDotZ[0] && 0 == strcmp(acDotZ, pc)) X break; X if (isalpha(*pc) && isupper(*pc)) X *pc = tolower(*pc); X } } X /* X * check a SEE ALSO entry for format, then check the manual page (ksb) X * it points to (if the format is correct). X */ void AlsoCheck(pWUThis, pcSee) WHATIS *pWUThis; char *pcSee; { X register char *pcOpen, *pcClose, *pcTail, *pcDot; X register DIR *pDI; X register struct direct *pDE; X register int iBase; X extern char *strchr(); X auto struct stat stPage; X auto char acLookFor[2*MAXPATHLEN+4]; X auto char acWrong[MAXPATHLEN+1]; X X if ((char *)0 == (pcOpen = strchr(pcSee, '('/*)*/)) || pcSee == pcOpen || !isdigit(pcOpen[1])) X return; X if ((char *)0 == (pcClose = strchr(pcSee, /*(*/')')) || '\000' != pcClose[1]) X return; X *pcOpen++ = '\000'; X *pcClose = '\000'; X X if ('/' == pcCat[0] || ('.' == pcCat[0] && '/' == pcCat[1])) X sprintf(acLookFor, "%s%ld/", pcCat, atol(pcOpen)); X else X sprintf(acLookFor, "%s/%s%ld/", pcRoot, pcCat, atol(pcOpen)); X StripFmt(acLookFor); X pcTail = acLookFor + strlen(acLookFor); X X sprintf(pcTail, "%s.%s", pcSee, pcOpen); X StripFmt(pcTail); X if ((char *)0 == (pcDot = strrchr(pcTail, '.'))) { X fprintf(stderr, "%s: %s: no dot in this string?\n", progname, pcTail); X exit(60); X } X iBase = pcDot - pcTail; X if (fCompress) { X (void)strcat(pcTail, acDotZ); X } X X if (-1 != stat(acLookFor, &stPage) && S_IFREG == (stPage.st_mode&S_IFMT)) { X /* found it */; X } else if (DropCase(pcTail, fCompress), -1 != stat(acLookFor, &stPage) && S_IFREG == (stPage.st_mode&S_IFMT)) { X /* found it */; X } else if (pcTail[-1] = '\000', (DIR *)0 == (pDI = opendir(acLookFor))) { X fprintf(stderr, "%s: opendir: %s: %s\n", progname, acLookFor, strerror(errno)); X } else { X /* printf("\tscan for %s in %s:\n", pcTail, acLookFor); */ X /* if the name appears in any case we quit, X * if it has the wrong section we record it, X */ X if (fCompress && (char *)0 != (pcDot = strrchr(pcTail, acDotZ[0])) && 0 == strcmp(acDotZ, pcDot)) { X *pcDot = '\000'; X } X acWrong[0] = '\000'; X while ((struct direct *)0 != (pDE = readdir(pDI))) { X if ((char *)0 != (pcDot = strrchr(pDE->d_name, acDotZ[0])) && 0 == strcmp(acDotZ, pcDot)) X *pcDot = '\000'; X else X pcDot = (char *)0; X if (0 == strcasecmp(pcTail, pDE->d_name)) { X break; X } X if (0 == strncasecmp(pcTail, pDE->d_name, iBase) && '.' == pDE->d_name[iBase]) { X (void)strcpy(acWrong, pDE->d_name); X } X } X closedir(pDI); X if ((struct direct *)0 != pDE) { X /* found it*/ ; X } else if ('\000' != acWrong[0]) { X printf("%s: %s.%s: SEE ALSO sites `%s\' found `%s\'\n", progname, pWUThis->pcbase, pWUThis->pcext, pcTail, acWrong); X X } else { X printf("%s: %s.%s: SEE ALSO sites `%s\', no such page\n", progname, pWUThis->pcbase, pWUThis->pcext, pcTail); X } X } X X *--pcOpen = '('; X *pcClose = ')'; } X /* X * scan the whatis structures for manual pages that don't exist (ksb) X * that are referenced in SEE ALSO lines X */ int AlsoScan(pWU, iCount) WHATIS *pWU; int iCount; { X register int i, ch; X register char *pcAlso, *pcStart; X X for (i = 0; i < iCount; ++i) { X if ((char *)0 == pWU[i].pcalso) { X /* no see also line in this page, skip it */ X continue; X } X X pcStart = (char *)0; X /* printf("%s.%s:\n", pWU[i].pcbase, pWU[i].pcext); */ X for (pcAlso = pWU[i].pcalso; '\000' != *pcAlso; ++pcAlso) { X if (',' != *pcAlso && !isspace(*pcAlso)) { X if ((char *)0 == pcStart) { X pcStart = pcAlso; X } X continue; X } X if ((char *)0 == pcStart) { X continue; X } X ch = *pcAlso; X *pcAlso = '\000'; X AlsoCheck(& pWU[i], pcStart); X *pcAlso = ch; X pcStart = (char *)0; X } X if ((char *)0 != pcStart) { X AlsoCheck(& pWU[i], pcStart); X } X } X return 1; } X X /* X * look at all the manual pages in a directory (ksb) X * X * Make sure they are compress'd or not... X * When we find a symbolic link we verify it. X * When we find a file we read it for makelinks, with that list in X * hand we will rebuild the hard an symbols links later. X * We delete lost links. X */ int AllDir(iSection, ppWU, piCount) WHATIS **ppWU; int iSection, *piCount; { X extern int alphasort(); X static char acDir[] = "."; X register struct direct *pDEPage, **ppDE; X register char *pcBase; X auto int i, l, k, npg; X auto FILE *fpFmt; X auto struct direct **ppDEPages; X auto char acLink[MAXPATHLEN+1]; X auto char acName[MAXPATHLEN+1]; X auto struct stat stCat; X auto int iw, iNeedFix; X auto WHATIS *pWU; X auto PATH PTTemp; X X *ppWU = (WHATIS *)0; X *piCount = iw = 0; X if (-1 == (npg = scandir(acDir, & ppDEPages, catSelect, alphasort))) { X fprintf(stderr, "%s: scandir: %s: %s\n", progname, acDir, strerror(errno)); X return 1; X } X if (0 == npg) { #if 0 X /* we don't output this because it makes all inits look broken X */ X fprintf(fpOut, "%s: no pages in section %d\n", progname, iSection); #endif X return 1; X } X X if (0 != FixCompress(npg, ppDEPages, & iNeedFix) || fJustComp) { X free((char *)ppDEPages); X return 0; X } X free((char *)ppDEPages); X X /* we cannot do the rest becuase FixCompress need to exec something X */ X if (!fExec && 0 != iNeedFix) { X return 0; X } X X if (-1 == (i = scandir(acDir, & ppDEPages, catSelect, alphasort))) { X fprintf(stderr, "%s: scandir: %s: %s\n", progname, acDir, strerror(errno)); X return 1; X } X X if (i > npg) { X fprintf(stderr, "%s: active manual system number of pages grew (%d > %d)\n", progname, i, npg); X } X npg = i; X X /* this might be a gross over allocation, sigh X * we will patch it with realloc later... X */ X if (0 == (pWU = (WHATIS *)malloc((unsigned)sizeof(WHATIS)*npg))) { X free((char *)ppDEPages); X fprintf(stderr, acNoMem, progname); X return 0; X } X X if (fVerbose) { X ScanDups(npg, ppDEPages, pWU); X } X X for (k = 0; k < npg; ++k) { X if ((struct direct *)0 == (pDEPage = ppDEPages[k])) { X continue; X } X if (-1 == LSTAT(pDEPage->d_name, & stCat)) { X continue; X } X /* ignore or bitch aboutr special files */ X switch (stCat.st_mode & S_IFMT) { #if defined(S_IFIFO) X case S_IFIFO: /* fifo */ X (void)fprintf(stderr, "%s: `%s\' is a fifo\n", progname, pDEPage->d_name); X ppDEPages[k] = 0; X free((char *)pDEPage); X continue; #endif /* no fifos */ X #if defined(S_IFSOCK) X case S_IFSOCK: /* socket */ X (void)fprintf(stderr, "%s: `%s\' is a socket\n", progname, pDEPage->d_name); X ppDEPages[k] = 0; X free((char *)pDEPage); X continue; #endif /* no sockets */ X case S_IFCHR: /* character special */ X case S_IFBLK: /* block special */ X fprintf(stderr, "%s: `%s\' is a special device\n", progname, pDEPage->d_name); X ppDEPages[k] = 0; X free((char *)pDEPage); X continue; X case S_IFDIR: /* directory */ X /* should get ., .., and OLD */ X ppDEPages[k] = 0; X free((char *)pDEPage); X continue; X #if defined(S_IFLNK) X case S_IFLNK: /* symbolic link */ X /* check later... */ X continue; #endif /* we can catch symbolic links for other reasons */ X X case 0: X case S_IFREG: /* regular */ X pcBase = pDEPage->d_name; X ppDEPages[k] = 0; X break; X } X X PTInit(&PTTemp, pcBase); X X /* skip bad extenders */ X if (! PTHasExt(&PTTemp)) { X fprintf(stdout, "%s: `%s\' has no section extension\n", progname, pcBase); X continue; X } X X /* open and read link info */ X if (PTIsComp(&PTTemp)) { X auto char acCmd[MAXPATHLEN+200]; X X sprintf(acCmd, "exec zcat \'%s\'", pcBase); X (void)fflush(stderr); X (void)fflush(stdout); X if (NULL == (fpFmt = popen(acCmd, "r"))) { X fprintf(stderr, "%s: popen: %s: %s\n", progname, acCmd, strerror(errno)); X continue; X } X i = WUGrok(fpFmt, &PTTemp, & pWU[iw]); X (void)pclose(fpFmt); X } else { X if (NULL == (fpFmt = fopen(pcBase, "r"))) { X fprintf(stderr, "%s: fopen: %s: %s\n", progname, pcBase, strerror(errno)); X continue; X } X i = WUGrok(fpFmt, &PTTemp, & pWU[iw]); X (void)fclose(fpFmt); X } X if (i != 0) { X /* cannot grok this page, skip it */ X continue; X } X X (void)sprintf(acName, "%s.%s%s", pWU[iw].pcbase, pWU[iw].pcext, fCompress ? acDotZ : ""); X X if (pWU[iw].isection != iSection) { X fprintf(fpOut, "%s: `%s\' might be in the wrong section\n", progname, pcBase); X } X X X /* here we've found (in pcBase) either X * a/ the primary link to a page (good) X * b/ a hard link to a page (fair) X * c/ link to out of date page (yucko) X * d/ a page whoose primary link is gone? (ouch!) X */ X if (0 != strcmp(pcBase, acName)) { /* (b)? */ X ppDE = FindDEnt(acName, ppDEPages, npg); X if ((struct direct **)0 != ppDE) { #if HAVE_SLINKS X if (0 == LSTAT(acName, &stCat)) { X switch ((stCat.st_mode & S_IFMT)) { X case S_IFLNK: X if (fExec && fMkLinks) { X sym2Hard(acName); X } X break; X case S_IFREG: X break; X default: X fprintf(stderr, "%s: type type error\n", progname); X abort(); X } X } #endif X if (!CmpFile(pcBase, acName)) { X fprintf(fpOut, "%s: would have scanned `%s\' as `%s\', but they are not identical\n", progname, pcBase, acName); X } else { X fprintf(fpOut, "%s: scan `%s\' as `%s\'\n", progname, pcBase, acName); X ppDEPages[k] = pDEPage; X pDEPage = *ppDE; X *ppDE = 0; X pcBase = pDEPage->d_name; X } X } else { /* (d) */ X fprintf(stderr, "%s: primary link to %s should be %s\n", progname, pcBase, acName); X if (fVerbose) { X (void)printf("%s: ln \"%s\" \"%s\"\n", progname, pcBase, acName); X } X if (fExec && fMkLinks && -1 == link(pcBase, acName)) { X fprintf(stderr, "%s: link: %s: %s\n", progname, acName, strerror(errno)); X } X } X } X MkLinks(acName, pWU+iw, ppDEPages, npg, &stCat, 0); X ++iw; X } X #if HAVE_SLINKS X /* the files left are links that point nowhere, or to files X * that do not what them (lost a subcommand?) X */ X for (l = 0; l < npg; ++l) { X if ((struct direct *)0 == (pDEPage = ppDEPages[l])) { X continue; X } X if (-1 == (i = readlink(pDEPage->d_name, acLink, MAXPATHLEN))) { X if (errno == EINVAL) { X (void)printf("%s: %s: manual page under the wrong name?\n", progname, pDEPage->d_name); X continue; X } X fprintf(stderr, "%s: readlink: %s: %s\n", progname, pDEPage->d_name, strerror(errno)); X continue; X } X acLink[i] = '\000'; X fprintf(fpOut, "%s: `%s\' is an unclaimed symbolic link", progname, pDEPage->d_name); X if (0 == access(acLink, R_OK)) { X fprintf(fpOut, " to `%s\'\n", acLink); X } else { X fprintf(fpOut, ", leading nowhere\n"); X } X } #endif X X /* now we can realloc the WHATIS array to the correct size X */ X if (iw < npg) { X if (0 == (pWU = (WHATIS *)realloc((char *)pWU, (unsigned)sizeof(WHATIS)*iw))) { X fprintf(stderr, "%s: realloc compression failed\n", progname); X return 0; X } X *ppWU = pWU; X } X *piCount = iw; X X return 1; } X /* X * this routine is kinda kludge. MkLinks wants to be in the dir it (ksb) X * is making the links for, so be cd there, do it, and come back. X * X * We could fork and wait if that would be more `safe' X * (this breaks if we are in a directory we cannot read, for example) X */ void ModLinks(pPTDest, pWU, ppDEPages, npg, pstCat, fRemove) PATH *pPTDest; WHATIS *pWU; struct direct **ppDEPages; int npg, fRemove; struct stat *pstCat; { X extern char *getwd(); X auto char acPwd[MAXPATHLEN+1]; X auto int fSave; X X (void)fflush(stdout); X if ((char *)0 == getwd(acPwd)) { X fprintf(stderr, "%s: %s\n", progname, acPwd); X return; X } X if (-1 == chdir(PTDir(pPTDest))) { X fprintf(stderr, "%s: chdir: %s: %s\n", progname, PTDir(pPTDest), strerror(errno)); X return; X } X X fSave = fMkLinks; X fMkLinks = 1; X MkLinks(PTLocal(pPTDest), pWU, ppDEPages, npg, pstCat, fRemove); X fMkLinks = fSave; X X if (-1 == chdir(acPwd)) { X fprintf(stderr, "%s: chdir: %s: %s\n", progname, acPwd, strerror(errno)); X exit(1); X } } Purdue chmod 0444 mkcat/scan.c || echo 'restore of mkcat/scan.c failed' Wc_c="`wc -c < 'mkcat/scan.c'`" test 27828 -eq "$Wc_c" || echo 'mkcat/scan.c: original size 27828, current size' "$Wc_c" fi true || echo 'restore of mkcat/genwhatis.c failed' echo End of part 2, continue with part 3 exit 0 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.