home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume24 / pucc-mk / part03 < prev    next >
Text File  |  1991-03-19  |  57KB  |  2,147 lines

  1. Subject:  v24i059:  Purdue shell turbo charger and manual installer, Part03/06
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: 6e30599b ca2f7cca 2ab41a55 d3a10a38
  5.  
  6. Submitted-by: Kevin Braunsdorf <ksb@cc.purdue.edu>
  7. Posting-number: Volume 24, Issue 59
  8. Archive-name: pucc-mk/part03
  9.  
  10. #!/bin/sh
  11. # This is part 03 of pucc-1c
  12. # ============= mkcat/genwhatis.c ==============
  13. if test ! -d 'mkcat'; then
  14.     echo 'x - creating directory mkcat'
  15.     mkdir 'mkcat'
  16. fi
  17. if test -f 'mkcat/genwhatis.c' -a X"$1" != X"-c"; then
  18.     echo 'x - skipping mkcat/genwhatis.c (File already exists)'
  19. else
  20. echo 'x - extracting mkcat/genwhatis.c (Text)'
  21. sed 's/^X//' << 'Purdue' > 'mkcat/genwhatis.c' &&
  22. /*
  23. X * Copyright 1990 Purdue Research Foundation, West Lafayette, Indiana
  24. X * 47907.  All rights reserved.
  25. X *
  26. X * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb
  27. X *
  28. X * This software is not subject to any license of the American Telephone
  29. X * and Telegraph Company or the Regents of the University of California.
  30. X *
  31. X * Permission is granted to anyone to use this software for any purpose on
  32. X * any computer system, and to alter it and redistribute it freely, subject
  33. X * to the following restrictions:
  34. X *
  35. X * 1. Neither the authors nor Purdue University are responsible for any
  36. X *    consequences of the use of this software.
  37. X *
  38. X * 2. The origin of this software must not be misrepresented, either by
  39. X *    explicit claim or by omission.  Credit to the authors and Purdue
  40. X *    University must appear in documentation and sources.
  41. X *
  42. X * 3. Altered versions must be plainly marked as such, and must not be
  43. X *    misrepresented as being the original software.
  44. X *
  45. X * 4. This notice may not be removed or altered.
  46. X */
  47. X
  48. /*  $Id: genwhatis.c,v 3.3 90/08/31 12:45:46 ksb Exp $
  49. X *
  50. X * genwhatis -- update the whatis database
  51. X */
  52. #include "machine.h"
  53. X
  54. #include <stdio.h>
  55. #include <ctype.h>
  56. #include <math.h>
  57. #include <errno.h>
  58. #include <sys/types.h>
  59. #include <sys/file.h>
  60. #include <sys/param.h>
  61. #include <fcntl.h>
  62. #if defined(SYSV)
  63. #include <ndir.h>
  64. #else
  65. #include <sys/dir.h>
  66. #endif
  67. #include <sys/stat.h>
  68. X
  69. #if !defined(MAXPATHLEN)
  70. #define MAXPATHLEN    1024
  71. #endif
  72. X
  73. #include "main.h"
  74. #include "pt.h"
  75. #include "genwhatis.h"
  76. #include "mkcat.h"
  77. X
  78. extern char *sys_errlist[];
  79. #define strerror(Me) sys_errlist[Me]
  80. extern char *strrchr(), *strcpy();
  81. X
  82. X
  83. extern char *malloc(), *realloc(), *calloc();
  84. extern char *mktemp(), *strcat(), *strcpy();
  85. extern int errno;
  86. extern int strlen();
  87. X
  88. static char acLockExt[] =    /* lock extender on db            */
  89. X    ".lock";
  90. X
  91. /*
  92. X * compare to whatis entries (the lowest left in each really)        (ksb)
  93. X */
  94. static int
  95. wucmp(pWULeft, pWURight)
  96. register WHATIS *pWULeft, *pWURight;
  97. {
  98. X    register int i;
  99. X
  100. X    i = pWULeft->ppclist[0][0] - pWURight->ppclist[0][0];
  101. X    if (i != 0)
  102. X        return i;
  103. X    i = strcmp(pWULeft->ppclist[0], pWURight->ppclist[0]);
  104. X    if (i != 0)
  105. X        return i;
  106. X    i = pWULeft->isection - pWURight->isection;
  107. X    if (i != 0)
  108. X        return i;
  109. X    return strcmp(pWULeft->pcext, pWURight->pcext);
  110. }
  111. X
  112. /*
  113. X * remove the first whatis entry in a queue                (ksb)
  114. X */
  115. static void
  116. wudequeue(pWU)
  117. WHATIS *pWU;
  118. {
  119. X    if (0 == pWU->ilen) {
  120. X        fprintf(stderr, "%s: internal error\n", progname);
  121. X        exit(20);
  122. X    }
  123. X    pWU->ilen -= 1;
  124. X    pWU->ppclist += 1;
  125. }
  126. X
  127. /*
  128. X * pull the lowest string off the queues                (ksb)
  129. X */
  130. static WHATIS *
  131. wufindmin(iLen, pWUWhats)
  132. int iLen;
  133. WHATIS *pWUWhats;
  134. {
  135. X    register WHATIS *pWULow;
  136. X    register int i, cp;
  137. X
  138. X    pWULow = (WHATIS *)0;
  139. X    for (i = 0; i < iLen; ++i) {
  140. X        /* spent or error page -- ignore it now
  141. X         */
  142. X        if (0 == pWUWhats[i].ilen)
  143. X            continue;
  144. X        if ((WHATIS *)0 == pWULow) {
  145. X            pWULow = & pWUWhats[i];
  146. X            continue;
  147. X        }
  148. X        /* this first case can happen if we were given two
  149. X         * copies of the same page to update... *sigh*
  150. X         */
  151. X        if (0 == (cp = wucmp(& pWUWhats[i], pWULow))) {
  152. X            wudequeue(& pWUWhats[i]);
  153. X        } else if (cp < 0) {
  154. X            pWULow = & pWUWhats[i];
  155. X        }
  156. X    }
  157. X    return pWULow;
  158. }
  159. X
  160. /*
  161. X * output a whatis entry                        (ksb)
  162. X */
  163. static void
  164. wudump(fp, pWU)
  165. FILE *fp;
  166. WHATIS *pWU;
  167. {
  168. X    fprintf(fp, "%s (%s)\t- %s\n", pWU->ppclist[0], pWU->pcext, pWU->pcsummary);
  169. }
  170. X
  171. /*
  172. X * read a whatis record from a file, cannot be longer than 1024        (ksb)
  173. X * characters
  174. X */
  175. static WHATIS *
  176. nextwu(fp)
  177. register FILE *fp;
  178. {
  179. X    static WHATIS WURet;
  180. X    static char *apcFake[2];
  181. X    static char acLine[1024];
  182. X    register char *pcLine;
  183. X
  184. X    if (feof(fp) || (char *)0 == (pcLine = fgets(acLine, 1024, fp))) {
  185. X        return (WHATIS *)0;
  186. X    }
  187. X
  188. X    /* we do not copy bogus entries that the old makewhatis program
  189. X     * would generate... " (5)     -devices - UUCP ...."
  190. X     * (for L-devices)
  191. X     */
  192. X    while (isspace(*pcLine))
  193. X        ++pcLine;
  194. X    apcFake[0] = pcLine;
  195. X    while (! isspace(*pcLine) && '\000' != *pcLine)
  196. X        ++pcLine;
  197. X
  198. X    do {
  199. X        *pcLine++ = '\000';
  200. X    } while (isspace(*pcLine));
  201. X
  202. X    /* should have "(Xn) - <stuff>" now */
  203. X    if ('('/*)*/ != *pcLine)
  204. X        return nextwu(fp);
  205. X    while ('\b' == pcLine[1] && '(' == pcLine[2]) /*)*/
  206. X        pcLine += 2;
  207. X    ++pcLine;
  208. X    WURet.isection = atoi(pcLine);
  209. X    WURet.pcext = pcLine;
  210. X    while (/*(*/ ')' != *pcLine && '\000' != *pcLine)
  211. X        ++pcLine;
  212. X    if (')' != *pcLine)
  213. X        return nextwu(fp);
  214. X    *pcLine++ = '\000';
  215. X
  216. X    while ('-' != *pcLine && '\000' != *pcLine)
  217. X        ++pcLine;
  218. X    if ('-' != *pcLine)
  219. X        return nextwu(fp);
  220. X
  221. X    do {
  222. X        ++pcLine;
  223. X    } while (isspace(*pcLine) && '\000' != *pcLine);
  224. X    WURet.pcsummary = pcLine;
  225. X
  226. X    if ((char *)0 != (pcLine = strrchr(pcLine, '\n'))) {
  227. X        *pcLine = '\000';
  228. X    }
  229. X
  230. X    WURet.ppclist = & apcFake[0];
  231. X    WURet.ilen = 1;
  232. X
  233. X    return & WURet;
  234. }
  235. X
  236. /*
  237. X * update the whatis database, use install (of course)            (ksb)
  238. X * we flock the whatis database so two people can run this at
  239. X * about the same time...
  240. X *
  241. X * Take    Old    Del    Add
  242. X * ~    n    n    n
  243. X * Old    y    n    n
  244. X * ~    n    y    n
  245. X * ~    y    y    n
  246. X * Add    n    n    y
  247. X * Add    y    n    y
  248. X * Add    n    y    y
  249. X * Add    y    y    y
  250. X */
  251. int
  252. MergeWhatis(fpOldWhatis, iLenNew, pWUWhatsNew, iLenDel, pWUWhatsGone, fpNewWhatis)
  253. FILE *fpOldWhatis, *fpNewWhatis;
  254. int iLenNew, iLenDel;
  255. WHATIS *pWUWhatsNew, *pWUWhatsGone;
  256. {
  257. X    register WHATIS *pWUAdd, *pWUDel, *pWUOld;
  258. X    register int i, a;
  259. X
  260. X    pWUOld = nextwu(fpOldWhatis);
  261. X    pWUAdd = wufindmin(iLenNew, pWUWhatsNew);
  262. X    pWUDel = wufindmin(iLenDel, pWUWhatsGone);
  263. X    while ((WHATIS *)0 != pWUOld) {
  264. X        while ((WHATIS *)0 != pWUAdd && (i = wucmp(pWUAdd, pWUOld)) < 0) {
  265. X            wudump(fpNewWhatis, pWUAdd);
  266. X            wudequeue(pWUAdd);
  267. X            pWUAdd = wufindmin(iLenNew, pWUWhatsNew);
  268. X        }
  269. X        while ((WHATIS *)0 != pWUDel && (a = wucmp(pWUDel, pWUOld)) < 0) {
  270. X            wudequeue(pWUDel);
  271. X            pWUDel = wufindmin(iLenDel, pWUWhatsGone);
  272. X        }
  273. X        if ((WHATIS *)0 != pWUAdd && 0 == i) {
  274. X            wudump(fpNewWhatis, pWUAdd);
  275. X            wudequeue(pWUAdd);
  276. X            pWUAdd = wufindmin(iLenNew, pWUWhatsNew);
  277. X        } else if ((WHATIS *)0 != pWUDel && 0 == a) {
  278. X            wudequeue(pWUDel);
  279. X            pWUDel = wufindmin(iLenDel, pWUWhatsGone);
  280. X        } else {
  281. X            wudump(fpNewWhatis, pWUOld);
  282. X        }
  283. X        pWUOld = nextwu(fpOldWhatis);
  284. X    }
  285. X    while ((WHATIS *)0 != pWUAdd) {
  286. X        wudump(fpNewWhatis, pWUAdd);
  287. X        wudequeue(pWUAdd);
  288. X        pWUAdd = wufindmin(iLenNew, pWUWhatsNew);
  289. X    }
  290. X    return 0;
  291. }
  292. X
  293. /*
  294. X * modify the whatis database (add or delete lines)
  295. X */
  296. int
  297. ModWhatis(iLenAdd, pWUAdd, iLenDel, pWUDel)
  298. int iLenAdd, iLenDel;
  299. WHATIS *pWUAdd, *pWUDel;
  300. {
  301. X    register FILE *fpWhat, *fpNew;
  302. X    register int i;
  303. X    auto int fdLock;            /* for a flock        */
  304. X    auto char *pcNew, *pcEnd;
  305. X    auto char acNew[MAXPATHLEN+1];
  306. X    auto char acDest[MAXPATHLEN+1];
  307. X    auto char acCmd[4*MAXPATHLEN+200];
  308. #define PDELAY    2    /* sleep between peeks at the lock        */
  309. #define MAXLOOP    30    /* wait 60 seconds for another update        */
  310. X
  311. X    if ('/' == pcWhatis[0] || ('.' == pcWhatis[0] && '/' == pcWhatis[1])) {
  312. X        sprintf(acDest, "%s", pcWhatis);
  313. X    } else {
  314. X        sprintf(acDest, "%s/%s", pcRoot, pcWhatis);
  315. X    }
  316. X    pcEnd = acDest+strlen(acDest);
  317. X    (void)strcpy(pcEnd, acLockExt);
  318. X    if (fExec) {
  319. X        for (i = 0; i < MAXLOOP; ++i) {
  320. X            fdLock = open(acDest, O_EXCL|O_CREAT|O_WRONLY, 0660);
  321. X            if (-1 == fdLock && EEXIST == errno) {
  322. X                if (0 == i) {
  323. X                    fprintf(stderr, "%s: contending for database\n", progname);
  324. X                    (void)fflush(stderr);
  325. X                }
  326. X                sleep(PDELAY);
  327. X                continue;
  328. X            }
  329. X            if (-1 == fdLock) {
  330. X                fprintf(stderr, "%s: open: %s: %s\n", progname, acDest, strerror(errno));
  331. X                exit(1);
  332. X            }
  333. X            (void)close(fdLock);
  334. X            break;
  335. X        }
  336. X        if (MAXLOOP == i) {
  337. X            fprintf(stderr, "%s: ignoring lock file\n", progname);
  338. X        } else if (fVerbose) {
  339. X            fprintf(fpOut, "%s: locked %s\n", progname, acDest);
  340. X        }
  341. X    }
  342. X    *pcEnd = '\000';
  343. X
  344. X    if (NULL == (fpWhat = fopen(acDest, "r"))) {
  345. X        fprintf(stderr, "%s: fopen: %s: %s\n", progname, acDest, strerror(errno));
  346. X        if (NULL == (fpWhat = fopen("/dev/null", "r"))) {
  347. X            fprintf(stderr, "%s: fopen: /dev/null: %s\n", progname, strerror(errno));
  348. X            exit(1);
  349. X        }
  350. X    }
  351. X
  352. X    (void)strcpy(acNew, "/usr/tmp/mkwhatXXXXXX");
  353. X    if ((char *)0 == (pcNew = mktemp(acNew))) {
  354. X        fprintf(stderr, "%s: mktemp: no temp file\n", progname);
  355. X        exit(1);
  356. X    }
  357. X    if (NULL == (fpNew = fopen(pcNew, "w"))) {
  358. X        fprintf(stderr, "%s: fopen: %s: %s\n", progname, pcNew, strerror(errno));
  359. X        exit(1);
  360. X    }
  361. X
  362. X    i = MergeWhatis(fpWhat, iLenAdd, pWUAdd, iLenDel, pWUDel, fpNew);
  363. X    (void)fclose(fpWhat);
  364. X    (void)fclose(fpNew);
  365. X    if (0 != i) {
  366. X        return 1;
  367. X    }
  368. X
  369. X    (void) sprintf(acCmd, "cmp -s %s %s || INSTALL=\"%s\" install", acDest, pcNew, fVerbose ? "-v" : "");
  370. X    if ((char *)0 != apcModes[WHATISOWNER]) {
  371. X        (void)strcat(acCmd, " -o");
  372. X        (void)strcat(acCmd, apcModes[WHATISOWNER]);
  373. X    }
  374. X    if ((char *)0 != apcModes[WHATISGROUP]) {
  375. X        (void)strcat(acCmd, " -g");
  376. X        (void)strcat(acCmd, apcModes[WHATISGROUP]);
  377. X    }
  378. X    if ((char *)0 != apcModes[WHATISMODE]) {
  379. X        (void)strcat(acCmd, " -m");
  380. X        (void)strcat(acCmd, apcModes[WHATISMODE]);
  381. X    }
  382. X    (void)strcat(acCmd, " ");
  383. X    (void)strcat(acCmd, pcNew);
  384. X    (void)strcat(acCmd, " ");
  385. X    (void)strcat(acCmd, acDest);
  386. X
  387. X    if (fVerbose) {
  388. X        fprintf(fpOut, "%s: %s\n", progname, acCmd);
  389. X    }
  390. X    (void)fflush(stdout);
  391. X    if (fExec && 0 != system(acCmd)) {
  392. X        return 1;
  393. X    }
  394. X    if (fVerbose) {
  395. X        fprintf(fpOut, "%s: rm -f %s\n", progname, pcNew);
  396. X    }
  397. X    if (fExec) {
  398. X        (void) unlink(pcNew);
  399. X    }
  400. X    *pcEnd = acLockExt[0];
  401. X    if (fExec) {
  402. X        if (-1 == unlink(acDest)) {
  403. X            fprintf(stderr, "%s: unlink: %s: %s\n", progname, acDest, strerror(errno));
  404. X        } else if (fVerbose) {
  405. X            fprintf(fpOut, "%s: unlocked %s\n", progname, acDest);
  406. X        }
  407. X    }
  408. X
  409. X    return 0;
  410. }
  411. X
  412. /*
  413. X * make a new whatis database from all the cat info            (ksb)
  414. X */
  415. int
  416. NewWhatis(n, ppWU, piCounts)
  417. int n;
  418. WHATIS **ppWU;
  419. int *piCounts;
  420. {
  421. X    register WHATIS **ppWUNew;
  422. X    register FILE *fpNew;
  423. X    register int i, f;
  424. X    auto int fdLock;            /* for a create lock    */
  425. X    auto char *pcNew, *pcEnd;
  426. X    auto char acNew[MAXPATHLEN+1];
  427. X    auto char acDest[MAXPATHLEN+1];
  428. X    auto char acCmd[4*MAXPATHLEN+200];
  429. X
  430. X    fprintf(fpOut, "%s: %d sections ready for whatis\n", progname, n);
  431. X
  432. X    if ('/' == pcWhatis[0] || ('.' == pcWhatis[0] && '/' == pcWhatis[1])) {
  433. X        sprintf(acDest, "%s", pcWhatis);
  434. X    } else {
  435. X        sprintf(acDest, "%s/%s", pcRoot, pcWhatis);
  436. X    }
  437. X    pcEnd = acDest+strlen(acDest);
  438. X    (void)strcpy(pcEnd, acLockExt);
  439. X    if (fExec) {
  440. X        for (i = 0; i < MAXLOOP; ++i) {
  441. X            fdLock = open(acDest, O_EXCL|O_CREAT|O_WRONLY, 0660);
  442. X            if (-1 == fdLock && EEXIST == errno) {
  443. X                if (0 == i) {
  444. X                    fprintf(stderr, "%s: contending for database\n", progname);
  445. X                    (void)fflush(stderr);
  446. X                }
  447. X                sleep(PDELAY);
  448. X                continue;
  449. X            }
  450. X            if (-1 == fdLock) {
  451. X                fprintf(stderr, "%s: open: %s: %s\n", progname, acDest, strerror(errno));
  452. X                exit(1);
  453. X            }
  454. X            (void)close(fdLock);
  455. X            break;
  456. X        }
  457. X        if (MAXLOOP == i) {
  458. X            fprintf(stderr, "%s: ignoring lock file\n", progname);
  459. X        } else if (fVerbose) {
  460. X            fprintf(fpOut, "%s: locked %s\n", progname, acDest);
  461. X        }
  462. X    }
  463. X    *pcEnd = '\000';
  464. X
  465. X    (void)strcpy(acNew, "/usr/tmp/mkwhatXXXXXX");
  466. X    if ((char *)0 == (pcNew = mktemp(acNew))) {
  467. X        fprintf(stderr, "%s: mktemp: no temp file\n", progname);
  468. X        return 0;
  469. X    }
  470. X    if (NULL == (fpNew = fopen(pcNew, "w"))) {
  471. X        fprintf(stderr, "%s: fopen: %s: %s\n", progname, pcNew, strerror(errno));
  472. X        return 0;
  473. X    }
  474. X
  475. X    if (0 == (ppWUNew = (WHATIS **)malloc((unsigned)n*sizeof(WHATIS *)))) {
  476. X        fprintf(stderr, acNoMem, progname);
  477. X        return 0;
  478. X    }
  479. X    for (i = 0; i < n; ++i) {
  480. X        ppWUNew[i] = wufindmin(piCounts[i], ppWU[i]);
  481. X    }
  482. X    for (;;) {
  483. X        f = -1;
  484. X        for (i = 0; i < n; ++i) {
  485. X            if ((WHATIS *)0 == ppWUNew[i]) {
  486. X                continue;
  487. X            }
  488. X            if (f == -1 || wucmp(ppWUNew[f], ppWUNew[i]) > 0) {
  489. X                f = i;
  490. X            }
  491. X        }
  492. X        if (f == -1)
  493. X            break;
  494. X        wudump(fpNew, ppWUNew[f]);
  495. X        wudequeue(ppWUNew[f]);
  496. X        ppWUNew[f] = wufindmin(piCounts[f], ppWU[f]);
  497. X    }
  498. X    (void)fclose(fpNew);
  499. X
  500. X    (void) sprintf(acCmd, "cmp -s %s %s || INSTALL=\"%s\" install", acDest, pcNew, fVerbose ? "-v" : "");
  501. X    if ((char *)0 != apcModes[WHATISOWNER]) {
  502. X        (void)strcat(acCmd, " -o");
  503. X        (void)strcat(acCmd, apcModes[WHATISOWNER]);
  504. X    }
  505. X    if ((char *)0 != apcModes[WHATISGROUP]) {
  506. X        (void)strcat(acCmd, " -g");
  507. X        (void)strcat(acCmd, apcModes[WHATISGROUP]);
  508. X    }
  509. X    if ((char *)0 != apcModes[WHATISMODE]) {
  510. X        (void)strcat(acCmd, " -m");
  511. X        (void)strcat(acCmd, apcModes[WHATISMODE]);
  512. X    }
  513. X    (void)strcat(acCmd, " ");
  514. X    (void)strcat(acCmd, pcNew);
  515. X    (void)strcat(acCmd, " ");
  516. X    (void)strcat(acCmd, acDest);
  517. X    if (fVerbose) {
  518. X        fprintf(fpOut, "%s: %s\n", progname, acCmd);
  519. X    }
  520. X    (void)fflush(stdout);
  521. X    if (fExec && 0 != system(acCmd)) {
  522. X        return 1;
  523. X    }
  524. X    if (fVerbose) {
  525. X        fprintf(fpOut, "%s: rm -f %s\n", progname, pcNew);
  526. X    }
  527. X    if (fExec) {
  528. X        (void) unlink(pcNew);
  529. X    }
  530. X    *pcEnd = acLockExt[0];
  531. X    if (fExec) {
  532. X        if (-1 == unlink(acDest)) {
  533. X            fprintf(stderr, "%s: unlink: %s: %s\n", progname, acDest, strerror(errno));
  534. X        } else if (fVerbose) {
  535. X            fprintf(fpOut, "%s: unlocked %s\n", progname, acDest);
  536. X        }
  537. X    }
  538. X
  539. X    return 0;
  540. }
  541. X
  542. /*
  543. X * routine for qsort(3)                            (ksb)
  544. X */
  545. int
  546. mysort(ppcLeft, ppcRight)
  547. char **ppcLeft, **ppcRight;
  548. {
  549. X    return strcmp(*ppcLeft, *ppcRight);
  550. }
  551. X
  552. /*
  553. X * read the whatis info out of a formatted manual page            (ksb)
  554. X * this allows us to understand what should be installed
  555. X * or removed from the cat dirs and whatis info...
  556. X *
  557. X * return non-zero on error
  558. X */
  559. int
  560. WUGrok(fpFmt, pPTPage, pWU)
  561. FILE    *fpFmt;        /* file to read stuff out of        (in)    */
  562. PATH    *pPTPage;    /* raw page                (in)    */
  563. WHATIS    *pWU;        /* whatis entries to fill in        (out)    */
  564. {
  565. X    extern char *strsave();
  566. X    auto char acBase[MAXPATHLEN+1];
  567. X    auto char acSection[MAXPATHLEN+1];
  568. X    auto char acHeader[MAXPATHLEN+1];
  569. X    auto char **ppcLinks, *pcAlso, *pcSummary, *pcHSection;
  570. X    auto int iLen;
  571. X
  572. X    pWU->ilen = 0;
  573. X    (void)strcpy(acBase, PTBase(pPTPage));
  574. X    (void)strcpy(acSection, PTExt(pPTPage));
  575. X
  576. X    /* rdlinks leaves LN_PAD (char *)0's for us - how nice
  577. X     */
  578. X    if (0 != rdlinks(fpFmt, PTFull(pPTPage), & ppcLinks, & iLen, & pcAlso, acHeader)) {
  579. X        return 1;
  580. X    }
  581. X    pcSummary = ppcLinks[--iLen];    /* summary is last line    */
  582. X
  583. X    if (!SplitHead(acHeader, & pcHSection)) {
  584. X        fprintf(fpOut, "%s: `%s\' has a malformed header line\n", progname, PTLocal(pPTPage));
  585. X        return 1;
  586. X    }
  587. X    if (0 == strcasecmp(acSection, "man")) {
  588. X        if (!isdigit(*pcHSection)) {
  589. X            fprintf(fpOut, "%s: `%s\' header extender `%s\' not numeric\n", progname, PTLocal(pPTPage), pcHSection);
  590. X        }
  591. X        (void)strcpy(acSection, pcHSection);
  592. X    } else if (0 != strcasecmp(acSection, pcHSection)) {
  593. X        fprintf(fpOut, "%s: `%s\' doesn\'t match its header extender `%s\' (not fatal)\n", progname, PTLocal(pPTPage), pcHSection);
  594. X    }
  595. X
  596. X    /* here we should search the alias list for the one in the header
  597. X     * (acHeader) and shift our weight to that one
  598. X     */
  599. X    if (0 != strcasecmp(acBase, acHeader)) {
  600. X        register int i;
  601. X        for (i = 0; i < iLen; ++i) {
  602. X            if (0 != strcasecmp(ppcLinks[i], acHeader)) {
  603. X                continue;
  604. X            }
  605. X            (void)strcpy(acBase, ppcLinks[i]);
  606. X            break;
  607. X        }
  608. X        if (i == iLen) {
  609. X            fprintf(fpOut, "%s: `%s.%s\' doesn\'t match its header name `%s\' (not fatal)\n", progname, acBase, acSection, acHeader);
  610. X            ppcLinks[iLen++] = acHeader;
  611. X        }
  612. X    }
  613. X
  614. X    /* put on man name    */
  615. X    ppcLinks[iLen++] = pWU->pcbase = strsave(acBase);
  616. X    ppcLinks[iLen] = (char *)0;
  617. X    qsort((char *)ppcLinks, iLen, sizeof(char *), mysort);
  618. X    uniq(ppcLinks, & iLen);
  619. X
  620. X    /* for the big whatis update at the end we save these
  621. X     * (note we saved the base name already)
  622. X     */
  623. X    pWU->isection = atoi(acSection);
  624. X    pWU->ilen = iLen;
  625. X    pWU->ppclist = ppcLinks;
  626. X    pWU->pcsummary = pcSummary;
  627. X    pWU->pcext = strsave(acSection);
  628. X    pWU->pcalso = pcAlso;
  629. X
  630. X    return 0;
  631. }
  632. X
  633. /*
  634. X * remove the links that are in both pWUCool and pWUOld from pWUOld    (ksb)
  635. X * This keeps us from removing a link, then putting it back
  636. X * return the number of links in common
  637. X */
  638. int
  639. WUStrike(pWUOld, pWUCool)
  640. WHATIS *pWUOld, *pWUCool;
  641. {
  642. X    register int cnt, i, j, c;
  643. X
  644. X    cnt = 0;
  645. X    for (j = i = 0; j < pWUCool->ilen; ++j) {
  646. X        while (i < pWUOld->ilen &&
  647. X           0 < (c = strcmp(pWUCool->ppclist[j], pWUOld->ppclist[i]))) {
  648. X            ++i;
  649. X        }
  650. X        if (i == pWUOld->ilen)
  651. X            break;
  652. X        if (0 != c) {
  653. X            continue;
  654. X        }
  655. X        c = i;
  656. X        do {
  657. X            pWUOld->ppclist[c] = pWUOld->ppclist[c+1];
  658. X        } while (++c < pWUOld->ilen);
  659. X        --pWUOld->ilen;
  660. X        ++cnt;
  661. X    }
  662. X    return cnt;
  663. }
  664. Purdue
  665. chmod 0444 mkcat/genwhatis.c ||
  666. echo 'restore of mkcat/genwhatis.c failed'
  667. Wc_c="`wc -c < 'mkcat/genwhatis.c'`"
  668. test 15821 -eq "$Wc_c" ||
  669.     echo 'mkcat/genwhatis.c: original size 15821, current size' "$Wc_c"
  670. fi
  671. # ============= mkcat/format.c ==============
  672. if test -f 'mkcat/format.c' -a X"$1" != X"-c"; then
  673.     echo 'x - skipping mkcat/format.c (File already exists)'
  674. else
  675. echo 'x - extracting mkcat/format.c (Text)'
  676. sed 's/^X//' << 'Purdue' > 'mkcat/format.c' &&
  677. /*
  678. X * Copyright 1990 Purdue Research Foundation, West Lafayette, Indiana
  679. X * 47907.  All rights reserved.
  680. X *
  681. X * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb
  682. X *
  683. X * This software is not subject to any license of the American Telephone
  684. X * and Telegraph Company or the Regents of the University of California.
  685. X *
  686. X * Permission is granted to anyone to use this software for any purpose on
  687. X * any computer system, and to alter it and redistribute it freely, subject
  688. X * to the following restrictions:
  689. X *
  690. X * 1. Neither the authors nor Purdue University are responsible for any
  691. X *    consequences of the use of this software.
  692. X *
  693. X * 2. The origin of this software must not be misrepresented, either by
  694. X *    explicit claim or by omission.  Credit to the authors and Purdue
  695. X *    University must appear in documentation and sources.
  696. X *
  697. X * 3. Altered versions must be plainly marked as such, and must not be
  698. X *    misrepresented as being the original software.
  699. X *
  700. X * 4. This notice may not be removed or altered.
  701. X */
  702. X
  703. /*  $Id: format.c,v 3.4 90/10/23 16:43:43 ksb Exp $
  704. X *
  705. X * format - make a cat (formatted) page from a man page
  706. X */
  707. #include "machine.h"
  708. X
  709. #include <stdio.h>
  710. #include <ctype.h>
  711. #include <math.h>
  712. #include <errno.h>
  713. #include <sys/types.h>
  714. #include <sys/file.h>
  715. #include <sys/param.h>
  716. #include <fcntl.h>
  717. #if defined(SYSV)
  718. #include <ndir.h>
  719. #else
  720. #include <sys/dir.h>
  721. #endif
  722. #include <sys/stat.h>
  723. X
  724. #if !defined(MAXPATHLEN)
  725. #define MAXPATHLEN    1024
  726. #endif
  727. X
  728. #include "main.h"
  729. #include "pt.h"
  730. #include "genwhatis.h"
  731. #include "readman.h"
  732. #include "scan.h"
  733. #include "mkcat.h"
  734. X
  735. extern FILE *popen();
  736. extern int errno;
  737. extern char *sys_errlist[];
  738. #define strerror(Me) sys_errlist[Me]
  739. extern char *strrchr(), *strcpy(), *strchr(), *strcat();
  740. extern char *malloc(), *mktemp();
  741. X
  742. #if !defined(F_OK)
  743. #define F_OK    0
  744. #endif
  745. X
  746. X
  747. char acRoot[] =            /* root of the formatted tree        */
  748. X    "/usr/man";
  749. char acCat[] =            /* cat dir basename            */
  750. X    "cat";
  751. char acWhatis[] =        /* whatis database to updtae        */
  752. X    "whatis";
  753. char acTemp[] =            /* a temp file with some space        */
  754. X    "/usr/tmp/mkcatXXXXXX";
  755. X
  756. char acDotZ[] =        /* compresses extender            */
  757. X    ".Z";
  758. X
  759. X
  760. /*
  761. X * warn of multiple manual pages, one name foo.1l, one foo.1 or        (ksb)
  762. X * some such (only for the name provided)
  763. X */
  764. void
  765. WarnMulti(iSection, ppcBase, pcSection)
  766. int iSection;
  767. char **ppcBase, *pcSection;
  768. {
  769. X    auto char acDir[MAXPATHLEN+1];
  770. X    register DIR *pDIRCat;
  771. X    register struct direct *pEnt;
  772. X    register char *pcFile, **ppcTemp;
  773. X    register char *pcHack;
  774. X
  775. X    if ('/' == pcCat[0] || ('.' == pcCat[0] && '/' == pcCat[1]))
  776. X        (void)sprintf(acDir, "%s%d", pcCat, iSection);
  777. X    else
  778. X        (void)sprintf(acDir, "%s/%s%d", pcRoot, pcCat, iSection);
  779. X    if ((DIR *)0 == (pDIRCat = opendir(acDir))) {
  780. X        fprintf(stderr, "%s: opendir: %s: %s\n", progname, acDir, strerror(errno));
  781. X        exit(1);
  782. X    }
  783. X
  784. X    while ((struct direct *)0 != (pEnt = readdir(pDIRCat))) {
  785. X        pcFile = pEnt->d_name;
  786. X        if ((char *)0 == (pcHack = strrchr(pcFile, '.'))) {
  787. X            /* ignore short files... or dirs */
  788. X            continue;
  789. X        }
  790. X
  791. X        /* ignore compress suffix on installed pages */
  792. X        if (0 == strcmp(acDotZ, pcHack)) {
  793. X            *pcHack = '\000';
  794. X            /* d_namlen is now wrong... do we care? */
  795. X            if ((char *)0 == (pcHack = strrchr(pcFile, '.'))) {
  796. X                continue;
  797. X            }
  798. X        }
  799. X        *pcHack++ = '\000';
  800. X
  801. X        /* ignore intro pages, `.', and `..'
  802. X         */
  803. X        if ('.' == pcFile[0] && ('\000' == pcFile[1] ||
  804. X             ('.' == pcFile[1] && '\000' == pcFile[2])) ||
  805. X            0 == IsOKBase(pcFile)) {
  806. X            continue;
  807. X        }
  808. X
  809. X        /* go through the (sorted!) list of names
  810. X         * quit on match with bad extender, or no more to check
  811. X         * (too bad the directory is not sorted here)
  812. X         */
  813. X        for (ppcTemp = ppcBase; (char *)0 != *ppcTemp; ++ppcTemp) {
  814. X            register int cp;
  815. X            cp = strcmp(pcFile, *ppcTemp);
  816. X            if (0 < cp)
  817. X                continue;
  818. X            if (0 > cp)
  819. X                break;
  820. X            if (ExtConflict(pcHack, pcSection)) {
  821. X                fprintf(fpOut, "%s: %s.%s and %s.%s both live in %s\n", progname, pcFile, pcHack, pcFile, pcSection, acDir);
  822. X            }
  823. X        }
  824. X    }
  825. X    (void)closedir(pDIRCat);
  826. }
  827. X
  828. X
  829. /*
  830. X * sort -u, kinda                            (ksb)
  831. X */
  832. uniq(ppcVec, piLen)
  833. char **ppcVec;
  834. int *piLen;
  835. {
  836. X    register char **ppcFrom, **ppcList;
  837. X
  838. X    ppcList = ppcVec;
  839. X    if ((char *)0 == ppcList[0])
  840. X        return;
  841. X    for (ppcFrom = ppcList+1; (char *)0 != *ppcFrom; ++ppcFrom) {
  842. X        if (0 != strcmp(*ppcFrom, *ppcList))
  843. X            *++ppcList = *ppcFrom;
  844. X        else
  845. X            --*piLen;
  846. X    }
  847. X    ppcList[1] = (char *)0;
  848. }
  849. X
  850. /*
  851. X * split off the two parts of BAR(1)                (ksb)
  852. X *                            ^^^ ^
  853. X * (also handles missformatted bold open parens)
  854. X */
  855. int
  856. SplitHead(pcHeader, ppcOpen)
  857. char *pcHeader, **ppcOpen;
  858. {
  859. X    register char *pcTemp, *pcClose, *pcOpen;
  860. X    register int retval = 1;
  861. X
  862. X    for (pcTemp = pcHeader; '\000' != *pcTemp; ++pcTemp) {
  863. X        if (isupper(*pcTemp)) {
  864. X            *pcTemp = tolower(*pcTemp);
  865. X        }
  866. X    }
  867. X
  868. X    if ((char *)0 != (pcOpen = strchr(pcHeader, '(') /*)*/)) {
  869. X        while ('\b' == pcOpen[1] && '('/*)*/ == pcOpen[2])
  870. X            pcOpen += 2;
  871. X        *pcOpen++ = '\000';
  872. X        if ((char *)0 != (pcClose = strchr(pcOpen, ')'))) {
  873. X            *pcClose = '\000';
  874. X        } else {
  875. X            retval = 0;
  876. X        }
  877. X    } else {
  878. X        retval = 0;
  879. X    }
  880. X    *ppcOpen = pcOpen;
  881. X    return retval;
  882. }
  883. X
  884. X
  885. /*
  886. X * format the given page with the given macro package            (ksb)
  887. X * put it in the named file
  888. X */
  889. static int
  890. MkFmt(pPTPage, pcTemp)
  891. PATH    *pPTPage;    /* raw page                (in)    */
  892. char    *pcTemp;    /* temp name to build it in        (in)    */
  893. {
  894. X    auto char acCmd[MAXPATHLEN*2+sizeof(acTemp)+400];
  895. X    auto FILE *fpFake;
  896. X
  897. #if !defined(R_OK)
  898. #define R_OK    4
  899. #endif
  900. X    if (0 != access(PTFull(pPTPage), R_OK)) {
  901. X        fprintf(stderr, "%s: access: %s: %s\n", progname, PTFull(pPTPage), strerror(errno));
  902. X        return 1;
  903. X    }
  904. X
  905. X    (void)sprintf(acCmd, "MK=\"%s\" mk -m%s %s >%s", fVerbose ? "" : "-s", (char *)0 != pcMkToken ? pcMkToken : "Mkcat", PTFull(pPTPage), pcTemp);
  906. X
  907. X    if (fVerbose) {
  908. X        fprintf(fpOut, "%s: %s\n", progname, acCmd);
  909. X    }
  910. X    (void)fflush(fpOut);
  911. X    (void)fflush(stderr);
  912. X
  913. X    if (fExec)
  914. X        return 0 != system(acCmd);
  915. X
  916. X    if (NULL == (fpFake = fopen(pcTemp, "w"))) {
  917. X        fprintf(stderr, "%s: fopen: %s: %s\n", progname, pcTemp, strerror(errno));
  918. X        return 1;
  919. X    }
  920. X    (void)fprintf(fpFake, "%s(", PTBase(pPTPage));
  921. X    (void)fprintf(fpFake, "%s) Fake man page\n\n%s\n", PTExt(pPTPage), acMark);
  922. X    (void)fprintf(fpFake, "\t%s - a dry run line\n\nfaked for your protection\n", PTBase(pPTPage));
  923. X    (void)fclose(fpFake);
  924. X    return 0;
  925. }
  926. X
  927. /*
  928. X * update an installed manual page, remove the old one and install new    (ksb)
  929. X *
  930. X * format it, all whatis lines to DEL or ADD list
  931. X * install the page
  932. X * remove or build links
  933. X * let caller update whatis as a group
  934. X *
  935. X * #1 why we remove an old page, if we replace all its links
  936. X *    in this case we have an old page called `OLD(1)' which
  937. X *    has a NAME section `old, new, other - things'.  The new page
  938. X *    has changed the names around (it happens) to call the page
  939. X *    `NEW(1)' with a NAME section `new, old, other -- things' or
  940. X *    we have added a new name `BETTER(1)' and kept all the old NAMES
  941. X *    ... in all cases we can remove the old page becasue we superceed
  942. X *    it!
  943. X *    If we do not cover *all* the old links we tell the user to run
  944. X *    rmcat on the `base' manual page or remove the collisions.
  945. X *    An example? cd(1) and sh(1).  If you run mkcat on cd(1) it will
  946. X *    tell you to remove sh(1) or remove the name `cd' in sh(1)!
  947. X *    *** As it should! ***
  948. X */
  949. int
  950. ModFmt(pPTPage, pWU, pWUDel)
  951. PATH *pPTPage;
  952. WHATIS *pWU, *pWUDel;
  953. {
  954. X    auto FILE *fpFmt;
  955. X    auto char acDest[MAXPATHLEN+1];
  956. X    auto char acTName[sizeof(acTemp)+sizeof(acDotZ)+1];
  957. X    auto char acCmd[MAXPATHLEN+sizeof(acTemp)+1000+400];
  958. X    auto int fWasThere;
  959. X    auto PATH PTDel;
  960. X    auto struct stat stDest;
  961. X
  962. X    if (! PTHasExt(pPTPage)) {
  963. X        fprintf(fpOut, "%s: `%s\' no manual section extender\n", progname, PTFull(pPTPage));
  964. X        return 1;
  965. X    }
  966. X
  967. X    if (! isdigit(*PTExt(pPTPage)) && 0 != strcasecmp("man", PTExt(pPTPage))) {
  968. X        fprintf(stderr, "%s: `%s\' missing section number in extender\n", progname, PTFull(pPTPage));
  969. X        return 1;
  970. X    }
  971. X
  972. X
  973. X    /* make a formatted page in a temp space and read the link info
  974. X     */
  975. X    (void)strcpy(acTName, acTemp);
  976. X    if ((char *)0 == mktemp(acTName)) {
  977. X        return 1;
  978. X    }
  979. X    if (fFormat) {
  980. X        /* MkFmt will fake a formatted copy if we are running under
  981. X         * -n, clever, eh?
  982. X         */
  983. X        if (0 != MkFmt(pPTPage, acTName)) {
  984. X            return 1;
  985. X        }
  986. X    } else {
  987. X        if (PTIsComp(pPTPage)) {
  988. X            (void)sprintf(acCmd, "zcat %s >%s", PTFull(pPTPage), acTName);
  989. X        } else {
  990. X            (void)sprintf(acCmd, "cp %s %s", PTFull(pPTPage), acTName);
  991. X        }
  992. X        if (fVerbose) {
  993. X            (void)printf("%s: %s\n", progname, acCmd);
  994. X        }
  995. X        if (0 != system(acCmd)) {
  996. X            fprintf(stderr, "%s: cp failed\n", progname);
  997. X            return 1;
  998. X        }
  999. X    }
  1000. X    if (NULL == (fpFmt = fopen(acTName, "r"))) {
  1001. X        fprintf(stderr, "%s: fopen: %s: %s\n", progname, PTFull(pPTPage), strerror(errno));
  1002. X        return 1;
  1003. X    }
  1004. X    if (0 != WUGrok(fpFmt, pPTPage,  pWU)) {
  1005. X        (void)fclose(fpFmt);
  1006. X        return 1;
  1007. X    }
  1008. X    (void)fclose(fpFmt);
  1009. X
  1010. X    if (0 != strcmp(pWU->pcbase, PTBase(pPTPage))) {
  1011. X        fprintf(fpOut, "%s: `%s\' should be named `%s.%s\' (repaired)\n", progname, PTFull(pPTPage), pWU->pcbase, pWU->pcext);
  1012. X    }
  1013. X
  1014. X    if ('/' == pcCat[0] || ('.' == pcCat[0] && '/' == pcCat[1])) {
  1015. X        (void)sprintf(acDest, "%s%d/%s.%s%s", pcCat, pWU->isection, pWU->pcbase, pWU->pcext, fCompress ? acDotZ : "");
  1016. X    } else {
  1017. X        (void)sprintf(acDest, "%s/%s%d/%s.%s%s", pcRoot, pcCat, pWU->isection, pWU->pcbase, pWU->pcext, fCompress ? acDotZ : "");
  1018. X    }
  1019. X    PTInit(&PTDel, acDest);
  1020. X
  1021. X    /* if an old one exists read it for links
  1022. X     * (no error if there is not one, might be a new file)
  1023. X     */
  1024. X    pWUDel->ilen = 0;
  1025. X    fWasThere = (-1 != LSTAT(acDest, &stDest));
  1026. X    if (fWasThere) {
  1027. X        register int r = 0;
  1028. X        if (PTIsComp(&PTDel)) {
  1029. X            (void)sprintf(acCmd, "exec zcat %s", acDest);
  1030. X            if (NULL != (fpFmt = popen(acCmd, "r"))) {
  1031. X                r = WUGrok(fpFmt, &PTDel, pWUDel);
  1032. X            }
  1033. X            (void)pclose(fpFmt);
  1034. X        } else {
  1035. X            if (NULL != (fpFmt = fopen(acDest, "r"))) {
  1036. X                r = WUGrok(fpFmt, &PTDel, pWUDel);
  1037. X            }
  1038. X            (void)fclose(fpFmt);
  1039. X        }
  1040. X        if (0 != r) {
  1041. X            printf("%s: %s: don\'t grok old man page\n", progname, acDest);
  1042. X            fWasThere = 0;
  1043. X        }
  1044. X    }
  1045. X    if (fDelete && !fWasThere) {
  1046. X        /* no page to remove, fail */
  1047. X        return 1;
  1048. X    }
  1049. X
  1050. X    (void)sprintf(acCmd, "INSTALL=\"%s\" install%s", fVerbose ? "-v" : "", fDelete ? " -R" : "");
  1051. X    if ((char *)0 != apcModes[PAGEOWNER]) {
  1052. X        (void)strcat(acCmd, " -o");
  1053. X        (void)strcat(acCmd, apcModes[PAGEOWNER]);
  1054. X    }
  1055. X    if ((char *)0 != apcModes[PAGEGROUP]) {
  1056. X        (void)strcat(acCmd, " -g");
  1057. X        (void)strcat(acCmd, apcModes[PAGEGROUP]);
  1058. X    }
  1059. X    if ((char *)0 != apcModes[PAGEMODE]) {
  1060. X        (void)strcat(acCmd, " -m");
  1061. X        (void)strcat(acCmd, apcModes[PAGEMODE]);
  1062. X    }
  1063. X
  1064. X    if (fDelete) {
  1065. X        if (0 != strcmp(pWUDel->pcbase, pWU->pcbase) || 0 != strcmp(pWUDel->pcext, pWU->pcext)) {
  1066. X            (void)printf("%s: `%s.%s\' doesn't match `%s.%s\'\n", progname, pWU->pcbase, pWU->pcext, pWUDel->pcbase, pWUDel->pcext);
  1067. X            (void)printf("%s: use `%s -Dv -f %s\' to remove page\n", progname, progname, acDest);
  1068. X            return 1;
  1069. X        }
  1070. X        ModLinks(&PTDel, pWUDel, (struct direct **)0, 0, &stDest, 1);
  1071. X        (void)WUStrike(pWU, pWUDel);
  1072. X    } else {
  1073. X        if (-1 == access(PTDir(&PTDel), F_OK)) {
  1074. X            MakeDir(PTDir(&PTDel), DIROWNER, DIRGROUP, DIRMODE);
  1075. X        }
  1076. X        /* remove common links from pWUDel, remove non-common
  1077. X         * from catdirs (links are not common if extender is
  1078. X         * different!)
  1079. X         */
  1080. X        if (fWasThere && 0 == strcmp(pWUDel->pcbase, pWU->pcbase)) {
  1081. X            if (0 == strcmp(pWUDel->pcext, pWU->pcext)) {
  1082. X                (void)WUStrike(pWUDel, pWU);
  1083. X            }
  1084. X            switch (stDest.st_mode & S_IFMT) {
  1085. X            case S_IFREG:
  1086. X                ModLinks(&PTDel, pWUDel, (struct direct **)0, 0, &stDest, 1);
  1087. X                break;
  1088. #if HAVE_SLINKS
  1089. X            case S_IFLNK:
  1090. X                fprintf(stderr, "%s: run -L before installing `%s\'\n", progname, acDest);
  1091. X                return 1;
  1092. #endif
  1093. X            default:
  1094. X                fprintf(stderr, "%s: %s: not a plain file or link\n", progname, acDest);
  1095. X                break;
  1096. X            }
  1097. X        }
  1098. X
  1099. X        WarnMulti(pWU->isection, pWU->ppclist, pWU->pcext);
  1100. X
  1101. X        /* compress the temp file if it needs it
  1102. X         */
  1103. X        if (fCompress) {
  1104. X            auto char acDoComp[MAXPATHLEN+100];
  1105. X            (void)sprintf(acDoComp, "exec compress -f %s", acTName);
  1106. X            if (0 != system(acDoComp)) {
  1107. X                fprintf(stderr, "%s: system: `%s\' failed\n", progname, acDoComp);
  1108. X                return 1;
  1109. X            }
  1110. X            if (-1 == access(acTName, F_OK)) {
  1111. X                (void)strcat(acTName, acDotZ);
  1112. X                if (-1 == access(acTName, R_OK)) {
  1113. X                    fprintf(stderr, "%s: compress lost my file, `%s\'\n", progname, acTName);
  1114. X                    return 1;
  1115. X                }
  1116. X            }
  1117. X        }
  1118. X
  1119. X        (void)strcat(acCmd, " ");
  1120. X        (void)strcat(acCmd, acTName);
  1121. X    }
  1122. X
  1123. X    (void)strcat(acCmd, " ");
  1124. X    (void)strcat(acCmd, acDest);
  1125. X
  1126. X    if (fVerbose) {
  1127. X        fprintf(fpOut, "%s: %s\n", progname, acCmd);
  1128. X    }
  1129. X    (void)fflush(stdout);
  1130. X    if (fExec && 0 != system(acCmd)) {
  1131. X        return 1;
  1132. X    }
  1133. X
  1134. X    /* now we have to put in the new links if needed
  1135. X     */
  1136. X    if (!fDelete) {
  1137. X        ModLinks(&PTDel, pWU, (struct direct **)0, 0, fWasThere ? &stDest : (struct stat *)0, 0);
  1138. X    }
  1139. X
  1140. X    /* install removed the temp file for us (unless the user set -c)
  1141. X     * in any event we try to unlink it, just in case install bombed
  1142. X     */
  1143. X    if (fVerbose) {
  1144. X        printf("%s: rm -f %s\n", progname, acTName);
  1145. X    }
  1146. X    (void)unlink(acTName);
  1147. X
  1148. X    return 0;
  1149. }
  1150. Purdue
  1151. chmod 0444 mkcat/format.c ||
  1152. echo 'restore of mkcat/format.c failed'
  1153. Wc_c="`wc -c < 'mkcat/format.c'`"
  1154. test 12839 -eq "$Wc_c" ||
  1155.     echo 'mkcat/format.c: original size 12839, current size' "$Wc_c"
  1156. fi
  1157. # ============= mk/setenv.c ==============
  1158. if test ! -d 'mk'; then
  1159.     echo 'x - creating directory mk'
  1160.     mkdir 'mk'
  1161. fi
  1162. if test -f 'mk/setenv.c' -a X"$1" != X"-c"; then
  1163.     echo 'x - skipping mk/setenv.c (File already exists)'
  1164. else
  1165. echo 'x - extracting mk/setenv.c (Text)'
  1166. sed 's/^X//' << 'Purdue' > 'mk/setenv.c' &&
  1167. #include <stdio.h>
  1168. #include "machine.h"
  1169. X
  1170. #define nil    ((char *)0)
  1171. extern char *strcpy(), *strcat();
  1172. X
  1173. /*
  1174. X * set an environment variable                        (ksb)
  1175. X * for unsetenv call with pchValue set to nil
  1176. X *
  1177. X * This routine modifies the global "environ" and will
  1178. X * build a new list if it must grow (it doesn't free the old one
  1179. X * since it may not have been malloc'd)  It will overwrite the
  1180. X * old value if the new one will fit, else is mallocs one large enough.
  1181. X *
  1182. X * returns nothing (an int)
  1183. X */
  1184. int
  1185. setenv(pchName, pchValue)
  1186. char *pchName, *pchValue;
  1187. {
  1188. X    extern char **environ, *calloc(), *malloc();
  1189. X    extern int strlen();
  1190. X    register char **ppch, *pch;
  1191. X    register int len, nv;
  1192. X
  1193. X    nv = 0;
  1194. X    len = strlen(pchName);
  1195. X    for (ppch = environ; nil != (pch = *ppch); ++ppch) {
  1196. X        if (0 == strncmp(pch, pchName, len) && '=' == pch[len]) {
  1197. X            break;
  1198. X        }
  1199. X        ++nv;
  1200. X    }
  1201. X
  1202. X    if (nil == pchValue) {
  1203. X        if (nil == pch)    {
  1204. X            return ;
  1205. X        }
  1206. X        while (nil != (ppch[0] = ppch[1])) {
  1207. X            ++ppch;
  1208. X        }
  1209. X        return ;
  1210. X    }
  1211. X
  1212. X    if (nil == pch) {
  1213. X        ++nv;
  1214. X        ppch = (char **)calloc(nv+1, sizeof(char *));
  1215. X        while (nv) {
  1216. X            --nv;
  1217. X            ppch[nv+1] = environ[nv];
  1218. X        }
  1219. X        environ = ppch;
  1220. X    }
  1221. X
  1222. X    len += strlen(pchValue) + 1;
  1223. X    if (nil == pch || strlen(pch) < len)
  1224. X        pch = malloc(len+1);
  1225. X    strcpy(pch, pchName);
  1226. X    strcat(pch, "=");
  1227. X    strcat(pch, pchValue);
  1228. X    *ppch = pch;
  1229. }
  1230. Purdue
  1231. chmod 0444 mk/setenv.c ||
  1232. echo 'restore of mk/setenv.c failed'
  1233. Wc_c="`wc -c < 'mk/setenv.c'`"
  1234. test 1292 -eq "$Wc_c" ||
  1235.     echo 'mk/setenv.c: original size 1292, current size' "$Wc_c"
  1236. fi
  1237. # ============= mk-lib/m-clean ==============
  1238. if test ! -d 'mk-lib'; then
  1239.     echo 'x - creating directory mk-lib'
  1240.     mkdir 'mk-lib'
  1241. fi
  1242. if test -f 'mk-lib/m-clean' -a X"$1" != X"-c"; then
  1243.     echo 'x - skipping mk-lib/m-clean (File already exists)'
  1244. else
  1245. echo 'x - extracting mk-lib/m-clean (Text)'
  1246. sed 's/^X//' << 'Purdue' > 'mk-lib/m-clean' &&
  1247. # the Clean target removes the junk generated by the other targets    (ksb)
  1248. X
  1249. $Clean(*):    ${rm-rm} -f %P.o
  1250. $Clean(*):    ${rm-rm} -f %P
  1251. $Clean(*):    ${make-make} %s clean
  1252. $Clean(*):    ${make-make} clean
  1253. Purdue
  1254. chmod 0444 mk-lib/m-clean ||
  1255. echo 'restore of mk-lib/m-clean failed'
  1256. Wc_c="`wc -c < 'mk-lib/m-clean'`"
  1257. test 191 -eq "$Wc_c" ||
  1258.     echo 'mk-lib/m-clean: original size 191, current size' "$Wc_c"
  1259. fi
  1260. # ============= mkcat/readman.c ==============
  1261. if test -f 'mkcat/readman.c' -a X"$1" != X"-c"; then
  1262.     echo 'x - skipping mkcat/readman.c (File already exists)'
  1263. else
  1264. echo 'x - extracting mkcat/readman.c (Text)'
  1265. sed 's/^X//' << 'Purdue' > 'mkcat/readman.c' &&
  1266. /*
  1267. X * $Id: readman.c,v 3.4 90/11/28 09:44:05 ksb Exp $
  1268. X *
  1269. X * Copyright 1990 Purdue Research Foundation, West Lafayette, Indiana
  1270. X * 47907.  All rights reserved.
  1271. X *
  1272. X * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb
  1273. X *
  1274. X * This software is not subject to any license of the American Telephone
  1275. X * and Telegraph Company or the Regents of the University of California.
  1276. X *
  1277. X * Permission is granted to anyone to use this software for any purpose on
  1278. X * any computer system, and to alter it and redistribute it freely, subject
  1279. X * to the following restrictions:
  1280. X *
  1281. X * 1. Neither the authors nor Purdue University are responsible for any
  1282. X *    consequences of the use of this software.
  1283. X *
  1284. X * 2. The origin of this software must not be misrepresented, either by
  1285. X *    explicit claim or by omission.  Credit to the authors and Purdue
  1286. X *    University must appear in documentation and sources.
  1287. X *
  1288. X * 3. Altered versions must be plainly marked as such, and must not be
  1289. X *    misrepresented as being the original software.
  1290. X *
  1291. X * 4. This notice may not be removed or altered.
  1292. X */
  1293. X
  1294. /*
  1295. X * routines to read a manual page and extract useful facts        (ksb)
  1296. X * $Compile: ${cc-cc} ${cc_debug--g} -DTEST %f -o %F
  1297. X */
  1298. #include "machine.h"
  1299. #include <stdio.h>
  1300. #include <ctype.h>
  1301. X
  1302. #include "main.h"
  1303. #include "readman.h"
  1304. #include "mkcat.h"
  1305. X
  1306. extern char *malloc(), *realloc(), *calloc(), *strrchr();
  1307. extern char *strcat(), *strcpy();
  1308. extern int strlen(), errno;
  1309. extern char *sys_errlist[];
  1310. #define strerror(Me) (sys_errlist[Me])
  1311. X
  1312. static char *apcFmts[] = {
  1313. X    "normal",
  1314. X    "bold",
  1315. X    "underline",
  1316. X    "bold & underlined",
  1317. X    "overstruck",
  1318. X    "bold & overstruck",
  1319. X    "underline & overstruck",
  1320. X    "bold & underlined & overstruck",
  1321. X    (char *)0
  1322. };
  1323. X
  1324. char acMark[] =        /* the line following this mark            */
  1325. X    "NAME";
  1326. char acAlso[] =        /* the pages we refer to follow this line    */
  1327. X    "SEE ALSO";
  1328. char acNoMem[] =        /* message for out of mem        */
  1329. X    "%s: out of memory\n";
  1330. X
  1331. # if 0
  1332. /* someday this will tell the user about unknown section names
  1333. X */
  1334. static char *apcHeaders[] = {
  1335. X    "AUTHOR",
  1336. X    "AUTHORS",
  1337. X    "AVAILABILITY",
  1338. X    "BUGS",
  1339. X    "CAVEATS",
  1340. X    "COMMANDS",
  1341. X    "COPYRIGHT",
  1342. X    "DESCRIPTION",
  1343. X    "DIAGNOSTICS",
  1344. X    "ENVIRONMENT",
  1345. X    "ENVIRONMENT VARIABLES",
  1346. X    "EXAMPLES",
  1347. X    "EXIT CODES",
  1348. X    "FILES",
  1349. X    "FUNCTIONS",
  1350. X    "HINTS",
  1351. X    "INITIALIZATION",
  1352. X    "LIMITATIONS",
  1353. X    "NAME",
  1354. X    "NOTES",
  1355. X    "OPTIONS",
  1356. X    "RETURN VALUES",
  1357. X    "ROUTINES",
  1358. X    "SEE ALSO",
  1359. X    "SUMMARY",
  1360. X    "SYNOPSIS",
  1361. X    "USAGE",
  1362. X    "VARIABLES",
  1363. X    (char *)0
  1364. };
  1365. #endif
  1366. X
  1367. /*
  1368. X * save a string in malloced memory                    (ksb)
  1369. X */
  1370. char *
  1371. strsave(pc)
  1372. char *pc;
  1373. {
  1374. X    register char *pcRet;
  1375. X
  1376. X    if ((char *)0 == (pcRet = malloc((unsigned)strlen(pc)+1))) {
  1377. X        fprintf(stderr, acNoMem, progname);
  1378. X        exit(1);
  1379. X    }
  1380. X    return strcpy(pcRet, pc);
  1381. }
  1382. X
  1383. /*
  1384. X * strip nroff nonsense of a string                    (ksb)
  1385. X */
  1386. int
  1387. StripFmt(pcBuf)
  1388. char *pcBuf;
  1389. {
  1390. X    register int fmt = FMT_NORMAL;
  1391. X    register char *pcScan;
  1392. X
  1393. X    for (pcScan = pcBuf; '\000' != *pcScan; ++pcScan) {
  1394. X        if ('\b' == pcScan[1]) {
  1395. X            if ('_' == pcScan[0]) {
  1396. X                fmt |= FMT_UNDERLINE;
  1397. X            } else if (pcScan[0] == pcScan[2]) {
  1398. X                fmt |= FMT_BOLD;
  1399. X            } else {
  1400. X                fmt |= FMT_OVERSTRIKE;
  1401. X            }
  1402. X            ++pcScan;
  1403. X            continue;
  1404. X        }
  1405. X        *pcBuf++ = *pcScan;
  1406. X    }
  1407. X    *pcBuf = '\000';
  1408. X    return fmt;
  1409. }
  1410. X
  1411. /*
  1412. X * compare a mark and a word which might be bold or something        (ksb)
  1413. X */
  1414. static int
  1415. markcmp(pcLine, pcMark, mLen)
  1416. char *pcLine, *pcMark;
  1417. int mLen;
  1418. {
  1419. X    register int i;
  1420. X
  1421. X    for (i = 0; i < mLen; ++i) {
  1422. X        while ('\000' != pcLine[0] && '\b' == pcLine[1]) {
  1423. X            pcLine += 2;
  1424. X        }
  1425. X        if (*pcLine != *pcMark)
  1426. X            return *pcLine - *pcMark;
  1427. X        ++pcLine, ++pcMark;
  1428. X    }
  1429. X    return 0;
  1430. }
  1431. X
  1432. /*
  1433. X * Build the names of the symbolic links the (soon to be installed)    (ksb)
  1434. X * manual page.
  1435. X * *we must* leave LN_PAD (char *)0's on the end of the vector
  1436. X * (one will be replaced be the basename before we sort -u them)
  1437. X * We will also find the header name:
  1438. X *    MKCAT(8)    ....    ....
  1439. X *      ^^^^^^^^
  1440. X * and save it, if pcHeader points to a buffer of about BUFSIZ.
  1441. X */
  1442. int
  1443. rdlinks(fpFmt, pcSrc, pppcVec, piLen, ppcAlso, pcHeader)
  1444. register FILE
  1445. X    *fpFmt;                /* input file to read        */
  1446. char    *pcSrc,                /* name to call it (for user)    */
  1447. X    ***pppcVec,            /* place to store list of names    */
  1448. X    **ppcAlso,            /* place to put see alsos    */
  1449. X    *pcHeader;            /* place to store header name    */
  1450. int    *piLen;                /* number of links        */
  1451. {
  1452. X    auto char *pcBuf, *pcAlsoLine;
  1453. X    register char *pcLine, *pcNewLine, **ppcLinks, **ppcMode;
  1454. X    register int i;
  1455. X    register char ch;
  1456. #define MODE_HEADER    0
  1457. #define MODE_SCAN    1
  1458. #define MODE_LINK    2
  1459. #define MODE_LINK2    3
  1460. #define MODE_BODY    4
  1461. #define MODE_ALSO    5
  1462. #define MISSING_PAREN    1
  1463. X    auto int iLen, iMode, iMissing, fHasDash;
  1464. X    auto char acLine[BUFSIZ];
  1465. X    extern char *strchr();
  1466. X
  1467. X    pcBuf = (char *)0;
  1468. X    pcAlsoLine = (char *)0;
  1469. X    iMode = MODE_HEADER;
  1470. X    iMissing = 0;
  1471. X    while ((char *)0 != (pcLine = fgets(acLine, BUFSIZ, fpFmt))) {
  1472. X        while (isspace(*pcLine) && '\000' != *pcLine) {
  1473. X            ++pcLine;
  1474. X        }
  1475. #if 0
  1476. X        printf("%s: %s", MODE_HEADER == iMode ? "header" : MODE_SCAN == iMode ? "scanner" : "links" , pcLine);
  1477. #endif
  1478. X        if (MODE_HEADER == iMode) {
  1479. X            register char *pcClose;
  1480. X
  1481. X            if ((char *)0 == (pcClose = strchr(pcLine, /*(*/')'))) {
  1482. X                if (0 == markcmp(pcLine, acMark, sizeof(acMark)-1)) {
  1483. X                    (void)fprintf(fpOut, "%s: %s: bad header line or %s line before header\n", progname, pcSrc, acMark);
  1484. X                    return 1;
  1485. X                }
  1486. X                if (strlen(pcLine) > 2) {
  1487. X                    iMissing |= MISSING_PAREN;
  1488. X                }
  1489. X                continue;
  1490. X            }
  1491. X            *++pcClose = '\000';
  1492. X            if ((char *)0 != pcHeader) {
  1493. X                (void)strcpy(pcHeader, pcLine);
  1494. X            }
  1495. X            iMode = MODE_SCAN;
  1496. X            continue;
  1497. X        }
  1498. X        if (MODE_SCAN == iMode) {
  1499. X            if (0 == markcmp(pcLine, acMark, sizeof(acMark)-1)) {
  1500. X                iMode = MODE_LINK;
  1501. X                ppcMode = &pcBuf;
  1502. X            }
  1503. X            continue;
  1504. X        }
  1505. X        if (MODE_BODY == iMode) {
  1506. X            if (0 == markcmp(pcLine, acAlso, sizeof(acAlso)-1)) {
  1507. X                iMode = MODE_ALSO;
  1508. X                ppcMode = &pcAlsoLine;
  1509. X            }
  1510. X            continue;
  1511. X        }
  1512. X        if ((char *)0 != (pcNewLine = strrchr(pcLine, '\n'))) {
  1513. X            do {
  1514. X                *pcNewLine = '\000';
  1515. X            } while (pcNewLine != pcLine && (--pcNewLine, isspace(*pcNewLine)));
  1516. X        }
  1517. X        if ('\000' == *pcLine) {
  1518. X            if (MODE_ALSO == iMode || (char **)0 == ppcAlso)
  1519. X                break;
  1520. X            iMode = MODE_BODY;
  1521. X            continue;
  1522. X        }
  1523. X
  1524. X        /* does the input line have the magic dash?
  1525. X         */
  1526. X        if (MODE_ALSO != iMode) {
  1527. X            register char *pcHack;
  1528. X
  1529. X            pcHack = pcLine;
  1530. X            while ((char *)0 != (pcHack = strchr(pcHack, '-'))) {
  1531. X                if ((pcHack == pcLine || isspace(pcHack[-1])) &&
  1532. X                     ('\000' == pcHack[1] || isspace(pcHack[1]))) {
  1533. X                    break;
  1534. X                }
  1535. X                ++pcHack;
  1536. X            }
  1537. X            fHasDash = ((char *)0 != pcHack);
  1538. X        } else {
  1539. X            fHasDash = 0;
  1540. X        }
  1541. X        if ((char *)0 == (*ppcMode)) {
  1542. X            iLen = strlen(pcLine)+1;
  1543. X            (*ppcMode) = malloc((unsigned)iLen+1);
  1544. X            if ((char *)0 == (*ppcMode)) {
  1545. X                fprintf(stderr, acNoMem, progname);
  1546. X                exit(1);
  1547. X            }
  1548. X            (void)strcpy((*ppcMode), pcLine);
  1549. X        } else {
  1550. X            register int temp;
  1551. X
  1552. X            if (MODE_LINK2 == iMode && fHasDash) {
  1553. X                fprintf(fpOut, "%s: %s: too many whatis entries at `%s\'\n", progname, pcSrc, pcLine);
  1554. X                break;
  1555. X            }
  1556. X            temp = strlen(pcLine)+1; /* put in a space */
  1557. X            (*ppcMode) = realloc((char *)(*ppcMode), (unsigned) iLen+temp+8);
  1558. X            if ((char *)0 == (*ppcMode)) {
  1559. X                fprintf(stderr, acNoMem, progname);
  1560. X                exit(1);
  1561. X            }
  1562. X            /* join lines that are hyphenated
  1563. X             * we know iLen is >2 we assume it is >>2
  1564. X             */
  1565. X            if ('-' == (*ppcMode)[iLen-2] && !isspace((*ppcMode)[iLen-3])) {
  1566. X                --iLen, --temp;
  1567. X                (void)strcpy(& (*ppcMode)[iLen-1], pcLine);
  1568. X            } else {
  1569. X                (*ppcMode)[iLen-1] = ' ';
  1570. X                (void)strcpy(& (*ppcMode)[iLen], pcLine);
  1571. X            }
  1572. X            iLen += temp;
  1573. X        }
  1574. X        if (fHasDash) {
  1575. X            iMode = MODE_LINK2;
  1576. X        }
  1577. X    }
  1578. X
  1579. X    if ((char *)0 == pcBuf) {
  1580. X        if (iMissing & MISSING_PAREN) {
  1581. X            (void)fprintf(fpOut, "%s: %s: header line missing parenthesis?\n", progname, pcSrc);
  1582. X        } else {
  1583. X            fprintf(fpOut, "%s: `%s\' has no %s line\n", progname, pcSrc, acMark);
  1584. X        }
  1585. X        return 1;
  1586. X    }
  1587. X
  1588. X    /* here we have something like "foo, bar, wham - meta words"
  1589. X     * we need to build a vector "foo", "bar", "wham", "meta words"
  1590. X     */
  1591. X    ch = '^';
  1592. X    iLen = 0;
  1593. X    for (pcLine = pcBuf; '\000' != *pcLine; ++pcLine) {
  1594. X        if (isspace(ch) && '-' == pcLine[0] && isspace(pcLine[1])) {
  1595. X            ++iLen;
  1596. X            break;
  1597. X        }
  1598. X        if (',' == *pcLine || (0 == iLen && ':' == *pcLine)) {
  1599. X            ++iLen;
  1600. X        }
  1601. X        ch = *pcLine;
  1602. X    }
  1603. X    if ('\000' == *pcLine || 0 == iLen) {
  1604. X        if ((char *)0 != pcBuf)
  1605. X            (void) free(pcBuf);
  1606. X        fprintf(fpOut, "%s: `%s\' has a bad %s line (too short)?\n", progname, pcSrc, acMark);
  1607. X        return 1;
  1608. X    }
  1609. X    ++iLen;
  1610. X    ppcLinks = (char **)calloc((unsigned) iLen+LN_PAD, sizeof(char *));
  1611. X    if ((char **)0 == ppcLinks) {
  1612. X        fprintf(stderr, acNoMem, progname);
  1613. X        exit(1);
  1614. X    }
  1615. X    iLen = 1;
  1616. X    ppcLinks[0] = pcBuf;
  1617. X    ch = '^';
  1618. X    for (pcLine = pcBuf; '\000' != *pcLine; ++pcLine) {
  1619. X        if (! (isspace(ch) && '-' == pcLine[0] && isspace(pcLine[1])) &&
  1620. X            ',' != *pcLine && !(iLen == 1 && ':' == *pcLine)) {
  1621. X            ch = *pcLine;
  1622. X            continue;
  1623. X        }
  1624. X        ch = *pcLine;
  1625. X        pcNewLine = pcLine;
  1626. X        while (pcNewLine > pcBuf && (--pcNewLine, isspace(*pcNewLine)))
  1627. X            *pcNewLine = '\000';
  1628. X        do {
  1629. X            *pcLine++ = '\000';
  1630. X        } while (isspace(*pcLine));
  1631. X        ppcLinks[iLen++] = pcLine;
  1632. X        if ('-' == ch) {
  1633. X            break;
  1634. X        }
  1635. X    }
  1636. X
  1637. X    /* search links for poor names, and warn.
  1638. X     */
  1639. X    for (i = 0; i < iLen-1; ++i) {
  1640. X        register char *pcTemp;
  1641. X        register int fmt;
  1642. X
  1643. X        pcTemp = ppcLinks[i];
  1644. X        fmt = StripFmt(pcTemp);
  1645. X        if (FMT_NORMAL != fmt) {
  1646. X            fprintf(fpOut, "%s: link `%s\' should not be %s\n", progname, pcTemp, apcFmts[fmt]);
  1647. X        }
  1648. X        while ('\000' != *pcTemp && !isspace(*pcTemp)) {
  1649. X            ++pcTemp;
  1650. X        }
  1651. X        if ('\000' != *pcTemp) {
  1652. X            register int j;
  1653. X            fprintf(fpOut, "%s: warning link `%s\' has a space in it (skipped)\n", progname, ppcLinks[i]);
  1654. X            --iLen;
  1655. X            for (j = i; j < iLen; ++j)
  1656. X                ppcLinks[j] = ppcLinks[j+1];
  1657. X            --i;
  1658. X            continue;
  1659. X        }
  1660. X        while ((char *)0 != (pcTemp = strrchr(ppcLinks[i], '/'))) {
  1661. X            /* eat trailing slash, paranoid */
  1662. X            if ('\000' == pcTemp[1]) {
  1663. X                *pcTemp = '\000';
  1664. X                continue;
  1665. X            }
  1666. X            ++pcTemp;
  1667. X            fprintf(fpOut, "%s: link `%s\' has a `/\' in it, changed to `%s\'\n", progname, ppcLinks[i], pcTemp);
  1668. X            ppcLinks[i] = pcTemp;
  1669. X            break;
  1670. X        }
  1671. X    }
  1672. X
  1673. X    (void)StripFmt(ppcLinks[iLen-1]);
  1674. X    ch = '^';
  1675. X    for (pcLine = pcBuf = ppcLinks[iLen-1]; '\000' != *pcLine; ++pcLine) {
  1676. X        if (isspace(*pcLine)) {
  1677. X            ch = ' ';
  1678. X            continue;
  1679. X        }
  1680. X        if (' ' == ch) {
  1681. X            *pcBuf++ = ' ';
  1682. X            ch = 'W';
  1683. X        }
  1684. X        *pcBuf++ = *pcLine;
  1685. X    }
  1686. X    *pcBuf = '\000';
  1687. X
  1688. X    ppcLinks[iLen] = (char *)0;
  1689. X    ppcLinks[iLen+1] = (char *)0;
  1690. X    *pppcVec = ppcLinks;
  1691. X    *piLen = iLen;
  1692. X    if ((char **)0 != ppcAlso) {
  1693. X        *ppcAlso = pcAlsoLine;
  1694. X    }
  1695. X    return 0;
  1696. }
  1697. X
  1698. #if TEST
  1699. char *progname = "test";
  1700. X
  1701. int
  1702. main(argc, argv)
  1703. int argc;
  1704. char **argv;
  1705. {
  1706. X    register int i, j;
  1707. X    auto int iCnt, iFailed;
  1708. X    auto char **ppcLinks, acHeader[1000], *pcAlso;
  1709. X    auto FILE *fpIn;
  1710. X
  1711. X    for (iFailed = 0, i = 1; i < argc; ++i) {
  1712. X        if ('-' == argv[i][0] && '\000' == argv[i][1]) {
  1713. X            fpIn = stdin;
  1714. X        } else if (NULL == (fpIn = fopen(argv[i], "r"))) {
  1715. X            fprintf(stderr, "%s: fopen: %s: %s\n", progname, argv[i], strerror(errno));
  1716. X            ++iFailed;
  1717. X            continue;
  1718. X        }
  1719. X        if (rdlinks(fpIn, argv[i], &ppcLinks, &iCnt, &pcAlso, acHeader)) {
  1720. X            printf("rdlinks fails for %s\n", argv[i]);
  1721. X            (void)fclose(fpIn);
  1722. X            ++iFailed;
  1723. X            continue;
  1724. X        }
  1725. X        if (fpIn == stdin)
  1726. X            clearerr(stdin);
  1727. X        else
  1728. X            (void)fclose(fpIn);
  1729. X        (void)printf("page %s: header `%s\', links %d\n", argv[i], acHeader, iCnt);
  1730. X        (void)printf("summary: `%s\'\n", ppcLinks[--iCnt]);
  1731. X        for (j = 0; j < iCnt; ++j) {
  1732. X            (void)printf("\t`%s\'\n", ppcLinks[j]);
  1733. X        }
  1734. X        if ((char *)0 != pcAlso) {
  1735. X            printf("see also:\n\t%s\n", pcAlso);
  1736. X        }
  1737. X    }
  1738. X    exit(iFailed);
  1739. }
  1740. #endif
  1741. Purdue
  1742. chmod 0444 mkcat/readman.c ||
  1743. echo 'restore of mkcat/readman.c failed'
  1744. Wc_c="`wc -c < 'mkcat/readman.c'`"
  1745. test 11371 -eq "$Wc_c" ||
  1746.     echo 'mkcat/readman.c: original size 11371, current size' "$Wc_c"
  1747. fi
  1748. # ============= mk/mk.1l ==============
  1749. if test -f 'mk/mk.1l' -a X"$1" != X"-c"; then
  1750.     echo 'x - skipping mk/mk.1l (File already exists)'
  1751. else
  1752. echo 'x - extracting mk/mk.1l (Text)'
  1753. sed 's/^X//' << 'Purdue' > 'mk/mk.1l' &&
  1754. .TH MK 1L LOCAL
  1755. .SH NAME
  1756. mk \- detect and execute shell commands in files
  1757. .SH SYNOPSIS
  1758. .B mk
  1759. [
  1760. .B \-AVachinsv
  1761. ] [
  1762. .BI \-D defn
  1763. ] [
  1764. .BI \-U undef
  1765. ] [
  1766. .BI \-d submarker
  1767. ] [
  1768. .BI \-e template
  1769. ] [
  1770. .BI \-l lines
  1771. ] [
  1772. .BI \-m marker
  1773. ] [
  1774. .BI \-t templates
  1775. ] [
  1776. .I file
  1777. ]
  1778. .SH DESCRIPTION
  1779. .I Mk
  1780. is a utility for detecting and executing shell commands within files.
  1781. It searches through the first
  1782. .I \fIlines\fP
  1783. (default 99) of named files,
  1784. looking for a \fImarker\fP (default \*(lqCompile\*(rq).
  1785. When the marker is located, the portion of the line on which it appears,
  1786. after the colon and up to a NEWLINE (or an occurrence of two sequential
  1787. unescaped dollar signs (``$$'')), is executed by issuing it to
  1788. .IR sh (1).
  1789. .PP
  1790. Normally, when the named files contain source language statements,
  1791. the commands are contained in lines that appear as comments to
  1792. the language processor.
  1793. This is merely a convention, however, and is not a
  1794. .I mk
  1795. requirement.
  1796. .PP
  1797. .I Mk
  1798. depends on the shell to do general parameter and
  1799. variable expansion.
  1800. However,
  1801. .I mk
  1802. does do some
  1803. .IR printf (3)\-like
  1804. string substitution.
  1805. These substitutions produce the file name in alternate forms.
  1806. The unlisted upper case letters below (DPQUX) each produce
  1807. the same expansion as the lower case version, but abort the
  1808. expansion if the string would be empty.
  1809. They begin with a percent sign (`%'):
  1810. .sp \n()Pu
  1811. .RS
  1812. .TS
  1813. l l.
  1814. %f    the full name of the \fIfile\fP specified on the \fImk\fP command line
  1815. %r    the RCS revision file for the specified \fIfile\fP
  1816. %d    the directory part of the specified \fIfile\fP
  1817. %p    the prefix portion of the specified \fIfile\fP
  1818. %q\fIc\fP    the prefix portion of \fIfile\fP upto \fIc\fP
  1819. %u\fIc\fP    the extension on \fIfile\fP after \fIc\fP
  1820. %x    the extension on the specified \fIfile\fP, if any
  1821. %y    the file type (f,d,b,c,l,s,p) as in \fIfind\fP(1)
  1822. %F    the base name of the \fIfile\fP, no prefix
  1823. %R    the RCS revision \fIfile\fP for the base name for the specified file
  1824. %Y\fIc\fP    the file type of \fIfile\fP must be \fIc\fP or abort
  1825. %~    the root of the default templates directory
  1826. %%    a literal percent sign
  1827. .TE
  1828. .RE
  1829. .PP
  1830. The values of \fImk\fP's command line options may be substituted, using
  1831. percent escapes.
  1832. Each of the options, \-\fBa\fP/\fBA\fP, \-\fBc\fP, \-\fBi\fP, \-\fBn\fP and
  1833. \-\fBv\fP/\fBs\fP, may be substituted via a percent escape that begins with the
  1834. option letter \- e. g., ``%c'' expands to ``\-c'' only if the \-\fBc\fP option
  1835. was specified in the call to \fImk\fP.
  1836. The upper case forms of these options suppress the leading dash.
  1837. Other command line parameters are available as listed below.
  1838. .sp \n()Pu
  1839. .RS
  1840. .TS
  1841. l l.
  1842. %b    the full path with which \fImk\fP was invoked
  1843. %e    expands to the ``\-e \fItemplate\fP'' option, if given
  1844. %l    expands to ``\-l \fIlines\fP'', if one was given
  1845. %m    the \fImarker\fP we are searching for
  1846. %o    the single letter switches supplied to \fImk\fP
  1847. %s    the \fIsubmarker\fP we are searching for
  1848. %t    expands to the ``\-t \fItemplates\fP'' option, if given
  1849. %B    the last component of the path with which \fImk\fP was invoked
  1850. %E    expands to the \fItemplate\fP option, if given
  1851. %L    expands to the \fIlines\fP option, if given
  1852. %M    the \fImarker\fP we are searching for in lower case
  1853. %O    the single letter switches supplied to \fImk\fP, no leading dash
  1854. %S    the \fIsubmarker\fP we are searching for in lower case
  1855. %T    expands to the \fItemplates\fP option, if given
  1856. .TE
  1857. .RE
  1858. .PP
  1859. If a percent escape fails to find the indicated data \- e. g., no
  1860. .I submarker
  1861. was specified \- it will silently cause the marked line being expanded to be
  1862. rejected.
  1863. .PP
  1864. All C\-like backslash (`\e') escape sequences are substituted.
  1865. These substitutions accommodate commands that require characters some
  1866. language processors might not allow in comments.
  1867. .PP
  1868. .I Mk
  1869. will also allow a resource limit to be set for each directive.
  1870. Here are the resources that can be limited:
  1871. .sp \n()Pu
  1872. .RS
  1873. .TS
  1874. l l l.
  1875. Resource    Description    Warning Signal
  1876. _
  1877. clock    wall clock seconds (done by \fImk\fP itself)    SIGALRM
  1878. core    core dump size in bytes    no warning signal
  1879. cpu    number of CPU seconds    SIGXCPU
  1880. data    data size in bytes    no warning signal
  1881. fsize    file size of any output file in bytes    SIGXFSZ
  1882. rss    resident pages in bytes    no warning signal
  1883. stack    stack size in bytes    SIGSEGV
  1884. .TE
  1885. .RE
  1886. .PP
  1887. These resources may be specified after the complete marker and submarker.
  1888. They are separated by commas from each other and the marker/submarker.
  1889. Two values may follow each resource name, separated from the
  1890. name by an equal sign (`=') \- a warning \fIlimit\fP and
  1891. an absolute \fImaximum\fP.
  1892. The
  1893. .I limit
  1894. and
  1895. .I maximum
  1896. must be separated by a slash (`/').
  1897. If only one value is specified, it is considered to be both the
  1898. .I limit
  1899. and the \fImaximum\fP.
  1900. .PP
  1901. When the directive's process reaches the \fIlimit\fP, it is
  1902. sent the indicated warning signal.
  1903. When the process reaches the \fImaximum\fP, it is sent a SIGKILL
  1904. signal.
  1905. When the
  1906. .I clock
  1907. values are the same,
  1908. .I mk
  1909. waits two seconds before sending the SIGKILL.
  1910. .PP
  1911. For example,
  1912. .sp \n()Pu
  1913. .RS
  1914. C $Run,cpu=300/360,core=0: %F 1 1000
  1915. .RE
  1916. .sp \n()Pu
  1917. would send to
  1918. .I sh
  1919. for execution (that's the meaning of the ``Run'' marker) the base name of
  1920. the file (effected with the ``%F'' substitution).
  1921. The base name executable would be supplied with the arguments ``1 1000''.
  1922. The CPU time limit would be 300 and the CPU time maximum, 360.
  1923. The maximum core dump size would be 0 bytes.
  1924. .PP
  1925. The exit status of the
  1926. .I mk
  1927. command is the count of the number of directives that exited non\-zero.
  1928. If a command is known (or intended) to fail then the expected exit code,
  1929. preceded by an equal sign (`='), may be specified after its marker
  1930. (``Fail'' in the following example).
  1931. .sp \n()Pu
  1932. .RS
  1933. # $Fail=1: %F "bad args"
  1934. .RE
  1935. .sp \n()Pu
  1936. When an exit code is specified, any other exit code will be treated
  1937. as a failure.
  1938. The special exit code `*' can be used to force
  1939. a command to always exit successfully.
  1940. Any exit code except the specified one will be considered a successful exit
  1941. if the specified code is prefixed with a tilde (`~').
  1942. In the following example, any nonzero exit code that ``%F'' issues will cause
  1943. .I mk
  1944. to issue a zero code:
  1945. .sp \n()Pu
  1946. .RS
  1947. # $NonZero=~0: %F "nonzero args"
  1948. .RE
  1949. .sp \n()Pu
  1950. .PP
  1951. The full form of an
  1952. .I mk
  1953. directive is
  1954. .sp \n()Pu
  1955. .RS
  1956. \fB$\fP \fImarker\fP[\fB(\fP\fIsubmarker\fP\fB)\fP] [\fB=\fP[\fB~\fP]\fIexit\-status\fP] [\fB,\fP\fIresource\fP\fB=\fP[\fIlimit\fP][\fB/\fP\fImaximum\fP]] \fB:\fP \fIcommand\fP [ \fB$$\fP ]
  1957. .RE
  1958. .sp \n()Pu
  1959. where the
  1960. .I resource
  1961. may be repeated to name and set multiple resource limits.
  1962. .PP
  1963. If no
  1964. .I marker
  1965. line can be found in the first \fIlines\fP (default 99)
  1966. of the file, a standard template file name is formed using the
  1967. \fB\-t\fItemplates\fR arguments.
  1968. The
  1969. .I templates
  1970. string is subjected to \fImk\fP's file name substitutions (see above).
  1971. .I Mk
  1972. then searches the file whose name emerges from that substitution for
  1973. \fImarkers\fP.
  1974. The special marker ``*'' matches, and is a match for, any marker
  1975. or submarker.
  1976. .SH OPTIONS
  1977. Note that options and arguments may be intermixed on the command line
  1978. to change the behavior of
  1979. .I mk
  1980. on a per file basis \- e. g., ``mk \-s foo.c \-v \-ddebug bar.c''.
  1981. .TP
  1982. .B \-A
  1983. Find all the marked lines that match the specified \fImarker\fP
  1984. and \fIsubmarker\fP, stop processing at the first command that is
  1985. successful.
  1986. .TP
  1987. .BI \-D defn
  1988. Give a definition for an environment variable.
  1989. The definition must have the form \fIident\fP\fB=\fP\fIvalue\fP.
  1990. .TP
  1991. .BI \-U undef
  1992. Remove a definition for the environment variable \fIundef\fP.
  1993. .TP
  1994. .B \-a
  1995. Find all the marked lines that match the specified \fImarker\fP
  1996. and \fIsubmarker\fP.
  1997. .TP
  1998. .B \-c 
  1999. Confirm the action before running it.
  2000. This will allow the user to see the command before it is executed.
  2001. A response of `y' or `Y' will cause the command to be run.
  2002. .TP
  2003. .BI \-d submarker
  2004. Look for the string \*(lq$\fImarker\fP(\fIsubmarker\fP):\*(rq in file.
  2005. White space is ignored.
  2006. A command line including \*(lq\-d debug\*(rq would match
  2007. \*(lq$\fImarker\fP(debug):\*(rq.
  2008. Submarkers in the file are ignored (do not take part in matching)
  2009. if no submarker is specified.
  2010. The \fIsubmarker\fP \*(lq*\*(rq matches all submarkers in the file.
  2011. .TP
  2012. .BI \-e template
  2013. This \fItemplate\fP will be searched before the file at hand.
  2014. This allows \fImk\fP to trap character special files with the %y macro.
  2015. .TP
  2016. .B \-h
  2017. Print a help message.
  2018. .TP
  2019. .B \-i
  2020. Ignore case when looking for \fImarker\fP and \fIsubmarker\fP.
  2021. .TP
  2022. .B \-l lines
  2023. Search
  2024. .I lines
  2025. lines rather than the default 99 for compilation markers.
  2026. .TP
  2027. .B \-m marker
  2028. Look for the string \*(rq\fB$\fP\fImarker\fP\fB:\fP\*(rq
  2029. as the delimiter for the compilation directive.
  2030. The default \fImarker\fP string is \*(lqCompile\*(rq.
  2031. Note that \fImarker\fP does not include either the leading dollar\-sign (`$')
  2032. or the trailing colon (`:').
  2033. The \fImarker\fP \*(lq*\*(rq matches all markers in the file.
  2034. .TP
  2035. .B \-n 
  2036. Do not execute the located commands.
  2037. This flag specifies that the resulting command(s) should not be executed,
  2038. but only printed on the standard output.
  2039. .TP
  2040. .B \-s
  2041. Be silent.
  2042. Executed commands are not output.
  2043. .TP
  2044. .BI \-t templates
  2045. The
  2046. .I templates
  2047. string produces a file name that will be searched for a
  2048. .I marker
  2049. line if no
  2050. .I marker
  2051. line is found in the file named on the
  2052. .I mk
  2053. call.
  2054. The percent escapes described above are all available for substitution
  2055. in this string, so that the template can be based on the extender of the
  2056. file being processed.
  2057. The default
  2058. \fItemplates\f string is ``/usr/local/lib/mk/def_%x''.
  2059. More than one template option my be specified.
  2060. .I Mk
  2061. searches for all of them in the order they are given.
  2062. .TP
  2063. .B \-v
  2064. Be verbose.
  2065. This is the opposite of \-\fBs\fP (above), and is implied by
  2066. the \-\fBn\fP option (above).
  2067. .TP
  2068. .B \-V
  2069. Be extra verbose.
  2070. Used only to debug \fImk\fP, or output the default templates configuration.
  2071. .SH EXAMPLES
  2072. .I Mk
  2073. is most commonly used to produce input for the shell.
  2074. The following lines might occur in a C program source file:
  2075. .sp \n()Pu
  2076. .RS
  2077. .nf
  2078. /*
  2079. X * $Compile: ${cc\-cc} ${CFLAGS\-\-O} \-o %F \-DFOO=1 %f
  2080. X * $Compile(debug): ${cc\-cc} ${CFLAGS\-\-g} \-o %F \-DDEBUG %f
  2081. X * $Run: %F /tmp/test
  2082. X * $Fail=1: %F /etc/passwd
  2083. X * $Limit,cpu=100,clock=600,fsize=10000: %F /tmp/test2
  2084. X */
  2085. .fi
  2086. .RE
  2087. .sp \n()Pu
  2088. If the file were called \*(lqfoo.c\*(rq, \fImk\fP,
  2089. invoked as
  2090. .sp \n()Pu
  2091. .RS
  2092. mk foo.c
  2093. .RE
  2094. .sp \n()Pu
  2095. would send
  2096. .I sh
  2097. the command:
  2098. .sp \n()Pu
  2099. .RS
  2100. cc \-O \-o foo \-DFOO=1 foo.c
  2101. .RE
  2102. .sp \n()Pu
  2103. With an invocation like
  2104. .sp \n()Pu
  2105. .RS
  2106. mk \-ddebug foo.c
  2107. .RE
  2108. .sp \n()Pu
  2109. the command
  2110. .sp \n()Pu
  2111. .RS
  2112. cc \-g \-o foo \-DDEBUG foo.c
  2113. .RE
  2114. .sp \n()Pu
  2115. would be sent to \fIsh\fP.
  2116. .PP
  2117. \fIMk\fP will output the default templates option if only \-\fBV\fP is
  2118. set:
  2119. .RS
  2120. .nf
  2121. mk: $Id: mk.1l,v 4.2 90/11/28 16:29:54 ksb Exp $
  2122. mk: %~ `/usr/local/lib/mk'
  2123. mk: -e `%~/type-%y:%~/pre-%x:%~/comma-%U,'
  2124. mk: -t `%~/file-%F:%~/dot-%x:%~/m-%M'
  2125. .fi
  2126. .RE
  2127. .SH SEE ALSO
  2128. sh(1),
  2129. setrlimit(2),
  2130. valid(1L).
  2131. .SH AUTHORS
  2132. S. McGeady, Intel, Inc., mcg@mipon2.intel.com
  2133. .sp 1
  2134. Kevin Braunsdorf, Purdue University Computing Center (ksb@cc.purdue.edu)
  2135. Purdue
  2136. chmod 0444 mk/mk.1l ||
  2137. echo 'restore of mk/mk.1l failed'
  2138. Wc_c="`wc -c < 'mk/mk.1l'`"
  2139. test 10944 -eq "$Wc_c" ||
  2140.     echo 'mk/mk.1l: original size 10944, current size' "$Wc_c"
  2141. fi
  2142. true || echo 'restore of mkcat/pt.c failed'
  2143. echo End of part 3, continue with part 4
  2144. exit 0
  2145.  
  2146. exit 0 # Just in case...
  2147.