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

  1. Subject:  v24i058:  Purdue shell turbo charger and manual installer, Part02/06
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: 2860dad0 69d21f52 6a03653e c3baab22
  5.  
  6. Submitted-by: Kevin Braunsdorf <ksb@cc.purdue.edu>
  7. Posting-number: Volume 24, Issue 58
  8. Archive-name: pucc-mk/part02
  9.  
  10. #!/bin/sh
  11. # This is part 02 of pucc-1c
  12. # ============= mk/mk.c ==============
  13. if test ! -d 'mk'; then
  14.     echo 'x - creating directory mk'
  15.     mkdir 'mk'
  16. fi
  17. if test -f 'mk/mk.c' -a X"$1" != X"-c"; then
  18.     echo 'x - skipping mk/mk.c (File already exists)'
  19. else
  20. echo 'x - extracting mk/mk.c (Text)'
  21. sed 's/^X//' << 'Purdue' > 'mk/mk.c' &&
  22. /*
  23. X * mk: detect and execute a compilation command 
  24. X *    (formerly called 'compile')
  25. X *
  26. X * example marker line:
  27. X *    $Compile: cc -c -O %f
  28. X *    $Compile (TEKECS): cc -c -DLOG -O %f
  29. X *    $Compile (DEBUG): cc -c -g -DDEBUG %f
  30. X *    $Compile=1: cc ....
  31. X *    $Compile,cpu=100: ....
  32. X *
  33. X * marker lines can also be drawn from a standard template
  34. X *
  35. X * This program searches for the first occurence of a marker (DEFLTMARK)
  36. X * in the first block of the named file(s), grabs the line on which
  37. X * the marker occurs, performs some filename substitutions on the line,
  38. X * and prints the line (typically a shell command line) on the stdout.
  39. X * It can also set resource limits for valid(1L).
  40. X *
  41. X * this programs currently makes lots of substitutions (see the man page).
  42. X *
  43. X * command-line switches:
  44. X *    see mk.m or main.c (or run mk -h)
  45. X *
  46. X * (c) Copyright 1983, Steven McGeady.  All rights reserved.
  47. X *
  48. X * Written by S. McGeady, Intel, Inc., mcg@mipon2.intel.com        (sm)
  49. X *          Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb        (ksb)
  50. X *
  51. X * This software is not subject to any license of the American Telephone
  52. X * and Telegraph Company or the Regents of the University of California.
  53. X *
  54. X * Permission is granted to anyone to use this software for any purpose on
  55. X * any computer system, and to alter it and redistribute it freely, subject
  56. X * to the following restrictions:
  57. X *
  58. X * 1. The authors are not held responsible for any consequences of the
  59. X *    use of this software.
  60. X *
  61. X * 2. The origin of this software must not be misrepresented, either by
  62. X *    explicit claim or by omission.  Credit to the authors must appear
  63. X *    in documentation and sources.
  64. X *
  65. X * 3. Altered versions must be plainly marked as such, and must not be
  66. X *    misrepresented as being the original software.
  67. X *
  68. X * 4. This notice may not be removed or altered.
  69. X *
  70. X * All bug fixes and improvements should be mailed to the authors,
  71. X * if you can find them.
  72. X */
  73. X
  74. #include <sys/param.h>
  75. #include <sys/types.h>
  76. #include <sys/stat.h>
  77. #include <sys/time.h>
  78. #include <signal.h>
  79. #include <stdio.h>
  80. #include <ctype.h>
  81. X
  82. #include "machine.h"
  83. #include "getopt.h"
  84. #include "main.h"
  85. #include "mk.h"
  86. #include "rlimsys.h"
  87. X
  88. #if !defined(MAXPATHLEN)
  89. #define MAXPATHLEN    1024
  90. #endif
  91. X
  92. #if RESOURCE
  93. #include <sys/resource.h>
  94. #include <sys/wait.h>
  95. #endif
  96. X
  97. #if defined(S_IFLNK)
  98. #define LSTAT lstat
  99. #else
  100. #define LSTAT stat
  101. #endif
  102. X
  103. extern char *strcpy(), *strrchr(), *strchr();
  104. extern char *getenv(), *rcsname();
  105. extern int errno;
  106. extern char *sys_errlist[];
  107. #define strerror(Me) (sys_errlist[Me])
  108. X
  109. char acOutMem[] = "%s: out of memory!\n";
  110. static int marklen, sublen;
  111. static char *curfile, chFType;
  112. static char acSearch[MAXPATHLEN+1];
  113. X
  114. #define    MAXLINE        BUFSIZ
  115. #define    LEADCHAR    '$'
  116. X
  117. #if defined(AUXDIR)
  118. static char acAuxDir[] = AUXDIR;
  119. #endif    /* mk's default home    */
  120. X
  121. #if defined(BEFORE)
  122. static char acDefBefore[] = BEFORE;
  123. #endif    /* default pre-scan    */
  124. X
  125. #if defined(TEMPL)
  126. static char acDefAfter[] = TEMPL;
  127. #endif    /* default post-scan    */
  128. X
  129. /*
  130. X * option a good version description                    (ksb)
  131. X */
  132. void
  133. Version()
  134. {
  135. X    fprintf(stdout, "%s: %s\n", progname, "$Id: mk.c,v 4.6 90/11/19 14:06:30 ksb Exp $");
  136. X    fprintf(stdout, "%s: %%~ `%s\'\n", progname, acAuxDir);
  137. X    fprintf(stdout, "%s: -e `%s\'\n", progname,
  138. X        (char *)0 != pchExamine ? pchExamine :
  139. X    
  140. #if defined(BEFORE)
  141. X        acDefBefore
  142. #else
  143. X        ""
  144. #endif    /* default pre-scan    */
  145. X    );
  146. X    fprintf(stdout, "%s: -t `%s\'\n", progname,
  147. X        (char *)0 != pchTemplates ? pchTemplates :
  148. #if defined(TEMPL)
  149. X        acDefAfter
  150. #else
  151. X        ""
  152. #endif    /* default post-scan    */
  153. X    );
  154. }
  155. X
  156. /*
  157. X * strip the case from an ident upto len or a char in pchStop        (ksb)
  158. X */
  159. void
  160. stripc(pchIdent, len, pchStop)
  161. char *pchIdent, *pchStop;
  162. int len;
  163. {
  164. X    register int i;
  165. X
  166. X    for (i = 0; i < len; ++i, ++pchIdent) {
  167. X        if ((char *)0 != strchr(pchStop, *pchIdent))
  168. X            break;
  169. X        if (isupper(*pchIdent))
  170. X            *pchIdent = tolower(*pchIdent);
  171. X    }
  172. }
  173. X
  174. /*
  175. X * eat the submark portion                         (sm)
  176. X */
  177. char *
  178. eatsub(pch, smark)
  179. char *pch;
  180. char *smark;
  181. {
  182. X    while (isspace(*pch)) {
  183. X        ++pch;
  184. X    }
  185. X
  186. X    if ((char *)0 != smark) {
  187. X        if ('(' != *pch) {
  188. X            return (char *)0;
  189. X        }
  190. X
  191. X        do
  192. X            ++pch;
  193. X        while (isspace(*pch));
  194. X        if (fCase)
  195. X            stripc(pch, sublen, "):");
  196. X        if ('*' == smark[0] && '\000' == smark[1]) {
  197. X            while (')' != pch[0] && '\000' != pch[0])
  198. X                ++pch;
  199. X        } else if ('*' == pch[0]) {
  200. X            ++pch;
  201. X        } else if (0 != strncmp(pch, smark, sublen)) {
  202. X            return (char *)0;
  203. X        } else {
  204. X            pch += sublen;
  205. X        }
  206. X
  207. X        while (isspace(*pch)) {
  208. X            ++pch;
  209. X        }
  210. X        if (')' != *pch) {
  211. X            return (char *)0;
  212. X        }
  213. X        do {
  214. X            ++pch;
  215. X        } while (isspace(*pch));
  216. X    } else if ('(' == *pch) {
  217. X        while (')' != *pch)
  218. X            ++pch;
  219. X        ++pch;
  220. X        while (isspace(*pch))
  221. X            ++pch;
  222. X    }
  223. X    return pch;
  224. }
  225. X
  226. /*
  227. X * find the end of the command string                    (ksb)
  228. X */
  229. void
  230. cut(pch)
  231. char *pch;
  232. {
  233. X    register char *q;
  234. X    register int c;
  235. X
  236. X    for (q = pch; '\000' != (c = *q); ++q) {
  237. X        switch (c) {
  238. X        case LEADCHAR:
  239. X            if (LEADCHAR == q[1]) {
  240. X        case '\n':
  241. X                q[0] = '\000';
  242. X                return;
  243. X            }
  244. X            break;
  245. X        case '\\':
  246. X            if ('\000' != q[1])
  247. X                ++q;
  248. X            break;
  249. X        default:
  250. X            break;
  251. X        }
  252. X    }
  253. }
  254. X
  255. /*
  256. X * copy chars with C escapes etc...                    (sm/ksb)
  257. X * since ANSI C won't let us write on constant strings we need fColon
  258. X * to stop on a colon durring template expansions
  259. X */
  260. char *
  261. translit(dst, src, fColon)
  262. register char *dst;
  263. register char *src;
  264. int fColon;
  265. {
  266. X    register char *tp;
  267. X    register char *xp, *pcParam;
  268. X    auto char sbPath[MAXPATHLEN+1];
  269. X    auto char acTemp[8];        /* buf for recursive calls    */
  270. X    register int i;
  271. X    register int num;
  272. X    auto int fGrave;        /* change %`name` into ${name}    */
  273. X
  274. #ifdef DEBUG
  275. X    fprintf(stderr, "translit(%s)\n", src);
  276. #endif
  277. X
  278. X    fGrave = 0;
  279. X    pcParam = (char *)0;
  280. X    while (*src) {
  281. X        switch (*src) {
  282. X        case '%':
  283. X            switch (*++src) {
  284. X            case 'A':
  285. X            case 'a':
  286. X                if (fAll || fFirst) {
  287. X                    if ('a' == *src)
  288. X                        *dst++ = '-';
  289. X                    *dst++ = fFirst ? 'A' : 'a';
  290. X                }
  291. X                break;
  292. X
  293. X            case 'B':
  294. X                strcpy(dst, progname);
  295. X                dst += strlen(dst);
  296. X                break;
  297. X
  298. X            case 'b':
  299. X                strcpy(dst, pathname);
  300. X                dst += strlen(dst);
  301. X                break;
  302. X
  303. X            case 'C':
  304. X            case 'c':    /* mk command line flags */
  305. X                if (fConfirm) {
  306. X                    if ('c' == *src)
  307. X                        *dst++ = '-';
  308. X                    *dst++ = 'c';
  309. X                }
  310. X                break;
  311. X
  312. X            case 'D':    /* if not remote, fail    */
  313. X            case 'd':    /* directory part    */
  314. X                if ((tp = strrchr(curfile, '/')) == NULL) {
  315. X                    if ('D' == *src)
  316. X                        if (debug)
  317. X                            fprintf(stderr, "%s: %s is not remote\n", progname, curfile);
  318. X                        return (char*)0;
  319. X                    break;
  320. X                }
  321. X                *tp = '\000';
  322. X                strcpy(dst, curfile);
  323. X                dst += strlen(curfile);
  324. X                *tp = '/';
  325. X                break;
  326. X
  327. X            case 'e':
  328. X                if ((char *)0 == pchExamine)
  329. X                    break;
  330. X                *dst++ = '-';
  331. X                *dst++ = 'e';
  332. X            case 'E':
  333. X                if ((char *)0 == pchExamine)
  334. X                    break;
  335. X                strcpy(dst, pchExamine);
  336. X                dst += strlen(dst);
  337. X                break;
  338. X
  339. X            case 'f':    /* full filename */
  340. X                strcpy(dst, curfile);
  341. X                dst += strlen(dst);
  342. X                break;
  343. X
  344. X            case 'F':    /* file part only */
  345. X                if ((tp = strrchr(curfile, '/')) != NULL) {
  346. X                    tp++;
  347. X                } else {
  348. X                    tp = curfile;
  349. X                }
  350. X                if ((xp = strrchr(tp, '.')) != NULL) {
  351. X                    *xp = '\000';
  352. X                    strcpy(dst, tp);
  353. X                    *xp = '.';
  354. X                } else {
  355. X                    strcpy(dst, tp);
  356. X                }
  357. X                dst += strlen(dst);
  358. X                break;
  359. X
  360. X            case 'I':
  361. X            case 'i':
  362. X                if (fCase) {
  363. X                    if ('i' == *src)
  364. X                        *dst++ = '-';
  365. X                    *dst++ = 'i';
  366. X                }
  367. X                break;
  368. X
  369. X            case 'l':
  370. X                *dst++ = '-';
  371. X                *dst++ = 'l';
  372. X            case 'L':
  373. X                sprintf(dst, "%d", lines);
  374. X                dst += strlen(dst);
  375. X                break;
  376. X
  377. X            case 'm':    /* marker we need */
  378. X            case 'M':    /* lower case marker for make rules */
  379. X                if ('*' == markstr[0])
  380. X                    *dst++ = '\\';
  381. X                strcpy(dst, markstr);
  382. X                if ('M' == *src && !fCase)
  383. X                    stripc(dst, marklen, "");
  384. X                dst += marklen;
  385. X                break;
  386. X
  387. X            case 'N':
  388. X            case 'n':
  389. X                if (!fExec) {
  390. X                    if ('n' == *src)
  391. X                        *dst++ = '-';
  392. X                    *dst++ = 'n';
  393. X                }
  394. X                break;
  395. X
  396. X            case 'o':
  397. X                *dst++ = '-';
  398. X            case 'O': /* all switches, none can fail */
  399. X                (void)translit(dst, "%A%C%I%N%V", 0);
  400. X                dst += strlen(dst);
  401. X                break;
  402. X
  403. X            case 'P':
  404. X            case 'p':    /* prefix        */
  405. X                acTemp[0] = '%';
  406. X                acTemp[1] = *src + ('Q' - 'P');
  407. X                acTemp[2] = '.';
  408. X                acTemp[3] = '\000';
  409. X                if ((tp = translit(dst, acTemp, 0)) == NULL) {
  410. X                    return (char *)0;
  411. X                }
  412. X                dst += strlen(dst);
  413. X                break;
  414. X
  415. X            case 'Q':
  416. X            case 'q':    /* prefix-x, mostly internal    */
  417. X                if ((tp = strrchr(curfile, '/')) != NULL) {
  418. X                    ++tp;
  419. X                } else {
  420. X                    tp = curfile;
  421. X                }
  422. X                if ('\000' == *++src)
  423. X                    break;
  424. X                if ((xp = strrchr(tp, *src)) != NULL) {
  425. X                    *xp = '\000';
  426. X                    strcpy(dst, curfile);
  427. X                    *xp = *src;
  428. X                } else {
  429. X                    if ('Q' == src[-1])
  430. X                        return (char *)0;
  431. X                    strcpy(dst, curfile);
  432. X                }
  433. X                dst += strlen(dst);
  434. X                break;
  435. X
  436. X            case 'R':    /* rcsbasename    */
  437. X                if ((tp = strrchr(curfile, '/')) != NULL) {
  438. X                    tp++;
  439. X                } else {
  440. X            case 'r':    /* rcsname    */
  441. X                    tp = curfile;
  442. X                }
  443. X                tp = rcsname(tp);
  444. X                if ((char *)0 == tp) {
  445. X                    if (debug)
  446. X                        fprintf(stderr, "%s: no rcsfile for %s\n", progname, curfile);
  447. X                    return (char *)0;
  448. X                }
  449. X                strcpy(dst, tp);
  450. X                dst += strlen(dst);
  451. X                break;
  452. X
  453. X            case 's': /* submarker we need */
  454. X            case 'S': /* lower case submarker for make rules */
  455. X                if ((char *)0 == submark) {
  456. X                    if (debug)
  457. X                        fprintf(stderr, "%s: no submarker for %s\n", progname, curfile);
  458. X                    return (char *)0;
  459. X                }
  460. X                if ('*' == submark[0])
  461. X                    *dst++ = '\\';
  462. X                strcpy(dst, submark);
  463. X                if ('S' == *src && !fCase)
  464. X                    stripc(dst, sublen, "");
  465. X                dst += sublen;
  466. X                break;
  467. X
  468. X            case 't':
  469. X                if ((char *)0 == pchTemplates)
  470. X                    break;
  471. X                *dst++ = '-';
  472. X                *dst++ = 't';
  473. X            case 'T':
  474. X                if ((char *)0 == pchTemplates)
  475. X                    break;
  476. X                strcpy(dst, pchTemplates);
  477. X                dst += strlen(dst);
  478. X                break;
  479. X
  480. X            case 'U':
  481. X            case 'u':    /* extender-x, mostly internal    */
  482. X                if ((tp = strrchr(curfile, '/')) != NULL) {
  483. X                    ++tp;
  484. X                } else {
  485. X                    tp = curfile;
  486. X                }
  487. X                if ('\000' == *++src)
  488. X                    break;
  489. X                if ((xp = strrchr(tp, *src)) != NULL) {
  490. X                    ++xp;
  491. X                    strcpy(dst, xp);
  492. X                    dst += strlen(dst);
  493. X                } else if ('U' == src[-1]) {
  494. X                    return (char *)0;
  495. X                }
  496. X                break;
  497. X
  498. X            case 'v':
  499. X                *dst++ = '-';
  500. X            case 'V':
  501. X                *dst++ = fVerbose ? 'v' : 's';
  502. X                if (debug)
  503. X                    *dst++ = 'V';
  504. X                break;
  505. X
  506. X            case 'W':
  507. X            case 'w':
  508. X                if ((tp = strrchr(acSearch, '/')) == NULL) {
  509. X                    if ('W' == *src)
  510. X                        if (debug)
  511. X                            fprintf(stderr, "%s: %s is not remote\n", progname, acSearch);
  512. X                        return (char*)0;
  513. X                    break;
  514. X                }
  515. X                *tp = '\000';
  516. X                strcpy(dst, acSearch);
  517. X                dst += strlen(dst);
  518. X                *tp = '/';
  519. X                break;
  520. X
  521. X            case 'X':
  522. X            case 'x':    /* extension        */
  523. X                acTemp[0] = '%';
  524. X                acTemp[1] = *src + ('U' - 'X');
  525. X                acTemp[2] = '.';
  526. X                acTemp[3] = '\000';
  527. X                if ((tp = translit(dst, acTemp, 0)) == NULL) {
  528. X                    return (char *)0;
  529. X                }
  530. X                dst += strlen(dst);
  531. X                break;
  532. X
  533. X            case 'Y':
  534. X                if ((*++src == '~') ? *++src == chFType : *src != chFType) {
  535. X                    if (debug)
  536. X                        fprintf(stderr, "%s: %s fails file type\n", progname, curfile);
  537. X                    return (char*)0;
  538. X                }
  539. X                break;
  540. X
  541. X            case 'y':    /* file type checks    */
  542. X                *dst++ = chFType;
  543. X                break;
  544. X
  545. X            case 'Z':
  546. X            case 'z':
  547. X                if ((char *)0 == (tp = strrchr(acSearch, '/'))) {
  548. X                    tp = acSearch;
  549. X                } else {
  550. X                    ++tp;
  551. X                }
  552. X                if ('\000' == *tp) {
  553. X                    if (debug)
  554. X                        fprintf(stderr, "%s: %s: not a template\n", progname, curfile);
  555. X                    return (char*)0;
  556. X                }
  557. X                strcpy(dst, tp);
  558. X                dst += strlen(dst);
  559. X                break;
  560. X
  561. X            case '~':    /* mk's home directory, so to speak */
  562. X                strcpy(dst, acAuxDir);
  563. X                dst += strlen(dst);
  564. X                break;
  565. X                
  566. X            case '\"':
  567. X            case '`':
  568. X                fGrave = 1;
  569. X                /* FALLTHROUGH */
  570. X            case '{':    /* } */
  571. X                *dst++ = '$';
  572. X                *dst++ = '{';    /* } */
  573. X                pcParam = dst;
  574. X                break;
  575. X            default:    /* unrecognized chars are copied thru */
  576. X                *dst++ = *src;
  577. X                break;
  578. X            }
  579. X            src++;
  580. X            break;
  581. X
  582. X        case '\\':
  583. X            switch (*++src) {
  584. X            case '\n':    /* how would this happen? */
  585. X                ++src;
  586. X                break;
  587. X            case 'n':    /* newline */
  588. X                *dst++ = '\n';
  589. X                ++src;
  590. X                break;
  591. X            case 't':
  592. X                *dst++ = '\t';
  593. X                ++src;
  594. X                break;
  595. X            case 'b':
  596. X                *dst++ = '\b';
  597. X                ++src;
  598. X                break;
  599. X            case 'r':
  600. X                *dst++ = '\r';
  601. X                ++src;
  602. X                break;
  603. X            case 'f':
  604. X                *dst++ = '\f';
  605. X                ++src;
  606. X                break;
  607. X            case 'v':
  608. X                *dst++ = '\013';
  609. X                ++src;
  610. X                break;
  611. X            case '\\':
  612. X                ++src;
  613. X            case '\000':
  614. X                *dst++ = '\\';
  615. X                break;
  616. X
  617. X            case '0': case '1': case '2': case '3':
  618. X            case '4': case '5': case '6': case '7':
  619. X                num = *src++ - '0';
  620. X                for (i = 0; i < 2; i++) {
  621. X                    if (! isdigit(*src)) {
  622. X                        break;
  623. X                    }
  624. X                    num <<= 3;
  625. X                    num += *src++ - '0';
  626. X                }
  627. X                *dst++ = num;
  628. X                break;
  629. X            case '8': case '9':
  630. X                /* 8 & 9 are bogus octals,
  631. X                 * cc makes them literals
  632. X                 */
  633. X                /*fallthrough*/
  634. X            default:
  635. X                *dst++ = *src++;
  636. X                break;
  637. X            }
  638. X            break;
  639. X        case '\"':
  640. X        case '`':
  641. X            if (fGrave) {
  642. X        case /*{*/ '}':
  643. X                *dst = '\000';
  644. X                if ((char *)0 != pcParam && (char *)0 == getenv(pcParam)) {
  645. X                    if (debug)
  646. X                        fprintf(stderr, "%s: %s: %s: not set\n", progname, curfile, pcParam);
  647. X                    return (char *)0;
  648. X                }
  649. X                pcParam = (char *)0;
  650. X                *dst++ = /*{*/ '}';
  651. X                ++src, fGrave = 0;
  652. X                break;
  653. X            }
  654. X            *dst++ = *src++;
  655. X            break;
  656. X        case ':':
  657. X            if (fColon) {
  658. X                src = "";
  659. X                break;
  660. X            }
  661. X            /*FALLTHROUGH*/
  662. X        default:
  663. X            *dst++ = *src++;
  664. X            break;
  665. X        }
  666. X    }
  667. X
  668. X    *dst = '\000';
  669. X    return dst;
  670. }
  671. X
  672. #define EXIT_EXACT    0x00    /* an exact match            */
  673. #define EXIT_ANY    0x01    /* any value                */
  674. #define EXIT_NOT    0x02    /* not what was given            */
  675. typedef struct ECnode {
  676. X    int ekey;        /* key for a match            */
  677. X    int ivalue;        /* value to match            */
  678. } CODE;
  679. X
  680. /*
  681. X * Find a marker line in the given file, put it in a buffer and        (sm/ksb)
  682. X * return a pointer to it.  Limitted to count lines.
  683. X */
  684. char *
  685. findmarker(fin, buf, pexitcode, count)
  686. register FILE *fin;
  687. char *buf;
  688. int count;
  689. CODE *pexitcode;
  690. {
  691. X    register char *pch;
  692. X    extern long atol();
  693. X
  694. X    if ((FILE *)0 == fin)
  695. X        return (char *)0;
  696. X    while ((char *)0 != (pch = fgets(buf, MAXLINE, fin))) {
  697. X        if (--count < 0) {
  698. X            pch = (char *)0;
  699. X            if (debug)
  700. X                fprintf(stderr, "%s: out of lines\n", progname);
  701. X            break;
  702. X        }
  703. X        pch = strchr(buf, LEADCHAR);
  704. X        if ((char *)0 == pch)
  705. X            continue;
  706. X
  707. X        do {
  708. X            ++pch;
  709. X        } while (isspace(*pch));
  710. X
  711. X        if (fCase)
  712. X            stripc(pch, marklen, ":("/*)*/);
  713. X        if ('*' == markstr[0] && '\000' == markstr[1]) {
  714. X            while ('(' != pch[0] && ':' != pch[0] && '\000' != pch[0])
  715. X                ++pch;
  716. X        } else if ('*' == pch[0]) {
  717. X            ++pch;
  718. X        } else if (0 != strncmp(markstr, pch, marklen)) {
  719. X            continue;
  720. X        } else {
  721. X            pch += marklen;
  722. X        }
  723. X
  724. X        pch = eatsub(pch, submark);
  725. X        if ((char *)0 == pch) {
  726. X            continue;
  727. X        }
  728. X
  729. X        /* set exit code */
  730. X        pexitcode->ekey = EXIT_EXACT;
  731. X        if ('=' == *pch) {
  732. X            ++pch;
  733. X            while (isspace(*pch))
  734. X                ++pch;
  735. X            if ('~' == *pch) {     /* ~code    */
  736. X                pexitcode->ekey = EXIT_NOT;
  737. X                ++pch;
  738. X            }
  739. X            while (isspace(*pch))
  740. X                ++pch;
  741. X            if ('*' == *pch) {     /* any code    */
  742. X                pexitcode->ekey |= EXIT_ANY;
  743. X                ++pch;
  744. X            } else {
  745. X                pexitcode->ivalue = atol(pch);
  746. X                if ('-' == *pch || '+' == *pch)
  747. X                    ++pch;
  748. X                while (isdigit(*pch))
  749. X                    ++pch;
  750. X            }
  751. X            while (isspace(*pch))
  752. X                ++pch;
  753. X        } else {
  754. X            pexitcode->ivalue = 0;
  755. X        }
  756. X
  757. #if RESOURCE
  758. X        /* get resource limits        */
  759. X        do {
  760. X            if (',' == *pch)
  761. X                ++pch;
  762. X            stripc(pch, MAXLINE, ",:");
  763. X            pch = rparse(pch);
  764. X        } while (',' == *pch);
  765. #else
  766. X        /* try to eat resource limits    */
  767. X        pch = strchr(pch, ':');
  768. X        if ((char *)0 == pch)
  769. X            continue;
  770. #endif /* resource limits */
  771. X
  772. X        if (':' != *pch)
  773. X            continue;
  774. X
  775. X        /* found mk marker */
  776. X        do {
  777. X            ++pch;
  778. X        } while (isspace(*pch));
  779. X        cut(pch);
  780. X        break;
  781. X    }
  782. X    return pch;
  783. }
  784. X
  785. /*
  786. X * place a variable in the environment                    (ksb)
  787. X */
  788. int
  789. define(pch)
  790. char *pch;
  791. {
  792. X    register char *p;
  793. X
  794. X    p = strchr(pch, '=');
  795. X    if ((char *)0 == p) {
  796. X        fprintf(stderr, "%s: missing `=\' for define of %s\n", progname, pch);
  797. X        return;
  798. X    }
  799. X    p[0] = '\000';
  800. X    setenv(pch, p+1);
  801. X    p[0] = '=';
  802. }
  803. X
  804. /*
  805. X * remove a variable from the environment                (ksb)
  806. X */
  807. int
  808. undefine(pch)
  809. char *pch;
  810. {
  811. X    setenv(pch, (char *)0);
  812. }
  813. X
  814. /*
  815. X * we have a filename and are ready to open and find a marker in it    (sm/ksb)
  816. X */
  817. int
  818. process(arg)
  819. char *arg;
  820. {
  821. X    auto FILE *fin, *fpSrc;
  822. X    auto char *pch, *pchSecond, *pchBefore, *pchLoop, *pchTrans;
  823. X    auto int count, fFoundOne;
  824. X    auto CODE exitcode;
  825. X    auto char buf[MAXLINE];
  826. X    auto char combuf[MAXLINE];
  827. X    auto struct stat stIn;
  828. X
  829. #if RESOURCE
  830. X    rinit();
  831. X    r_fTrace = debug;
  832. #endif /* resource limits */
  833. X
  834. X    curfile = arg;
  835. X    if ('-' == curfile[0] && '\000' == curfile[1]) {
  836. X        fprintf(stderr, "%s: stdin not supported\n", progname);
  837. X        return 1;
  838. X    }
  839. X    if (-1 == LSTAT(curfile, & stIn)) {
  840. X        fprintf(stderr, "%s: stat: %s: %s\n", progname, curfile, strerror(errno));
  841. X        return 1;
  842. X    }
  843. X    switch (stIn.st_mode & S_IFMT) {
  844. #if defined(S_IFLNK)
  845. X    case S_IFLNK:    /* symbolic link */
  846. X        chFType = 'l';
  847. X        break;
  848. #endif
  849. #if defined(S_IFIFO)
  850. X    case S_IFIFO:    /* fifo */
  851. X        chFType = 'p';
  852. X        break;
  853. #endif    /* no fifos */
  854. #if defined(S_IFSOCK)
  855. X    case S_IFSOCK:    /* socket */
  856. X        chFType = 's';
  857. X        break;
  858. #endif    /* no sockets */
  859. X    case S_IFDIR:    /* directory */
  860. X        chFType = 'd';
  861. X        break;
  862. X    case S_IFCHR:    /* character special */
  863. X        chFType = 'c';
  864. X        break;
  865. X    case S_IFBLK:    /* block special */
  866. X        chFType = 'b';
  867. X        break;
  868. X    case 0:
  869. X    case S_IFREG:    /* regular */
  870. X        chFType = 'f';
  871. X        break;
  872. X    default:
  873. X        fprintf(stderr, "%s: stat: %s: unknown file type?\n", progname, curfile);
  874. X        return 1;
  875. X    }
  876. X    if ('s' == chFType || 'd' == chFType) {
  877. X        /* dirs and sockets don't have text to read */
  878. X        fpSrc = (FILE *)0;
  879. X    } else if (NULL == (fpSrc = fopen(curfile, "r"))) {
  880. X        fprintf(stderr, "%s: fopen: %s: %s\n", progname, curfile, strerror(errno));
  881. X        return 1;
  882. X    }
  883. X
  884. X    if ((char *)0 == markstr) {
  885. X        if ('m' == progname[0] && 'k' == progname[1] && '\000' == progname[2])
  886. X            markstr = /*0m*/"Compile";
  887. X        else
  888. X            markstr = progname;
  889. X    }
  890. X    while (isspace(*markstr)) {
  891. X        ++markstr;
  892. X    }
  893. X    marklen = strlen(markstr);
  894. X    if (fCase)
  895. X        stripc(markstr, marklen, "");
  896. X
  897. X    if ((char *)0 != submark) {
  898. X        while (isspace(*submark)) {
  899. X            ++submark;
  900. X        }
  901. X        sublen = strlen(submark);
  902. X        if (fCase)
  903. X            stripc(submark, sublen, "");
  904. X    } else {
  905. X        sublen = 0;
  906. X    }
  907. X
  908. X    pchBefore = pchExamine;
  909. #if defined(BEFORE)
  910. X    if ((char *)0 == pchBefore) {
  911. X        pchBefore = acDefBefore;
  912. X    }
  913. #endif    /* default pre-scan    */
  914. X    pchSecond = pchTemplates;
  915. #if defined(TEMPL)
  916. X    if ((char *)0 == pchSecond) {
  917. X        pchSecond = acDefAfter;
  918. X    }
  919. #endif /* default templates    */
  920. X
  921. X    if (! fExec)
  922. X        fVerbose = 1;
  923. X    count = fFoundOne = 0;
  924. X    fin = (FILE *)0;
  925. X    do {
  926. X        pch = findmarker(fin, & buf[0], & exitcode, lines);
  927. X        if ((char *)0 == pch && (char *)0 != pchBefore) {
  928. X            do {
  929. X                pchLoop = strchr(pchBefore, ':');
  930. X                if ((char *)0 != pchLoop)
  931. X                    ++pchLoop;
  932. X                pchTrans = translit(acSearch, pchBefore, 1);
  933. X                pchBefore = pchLoop;
  934. X                if ((FILE *)0 != fin)
  935. X                    fclose(fin);
  936. X                fin = (char *)0 == pchTrans ? (FILE *)0 : fopen(acSearch, "r");
  937. X            } while ((FILE *)0 == fin && (char *)0 != pchBefore);
  938. X            if (debug && (FILE *)0 != fin) {
  939. X                fprintf(stderr, "%s: searching template %s\n", progname, acSearch);
  940. X            }
  941. X            if ((FILE *)0 != fin)
  942. X                continue;
  943. X        }
  944. X        if ((char *)0 == pch && (FILE *)0 != fpSrc) {
  945. X            if ((FILE*)0 != fin)
  946. X                fclose(fin);
  947. X            fin = fpSrc;
  948. X            fpSrc = (FILE *)0;
  949. X            acSearch[0] = '\000';
  950. X            if (debug) {
  951. X                fprintf(stderr, "%s: searching file %s\n", progname, curfile);
  952. X            }
  953. X            continue;
  954. X        }
  955. X        if ((char *)0 == pch && (char *)0 != pchSecond) {
  956. X            do {
  957. X                pchLoop = strchr(pchSecond, ':');
  958. X                if ((char *)0 != pchLoop)
  959. X                    ++pchLoop;
  960. X                pchTrans = translit(acSearch, pchSecond, 1);
  961. X                pchSecond = pchLoop;
  962. X                if ((FILE *)0 != fin)
  963. X                    fclose(fin);
  964. X                fin = (char *)0 == pchTrans ? (FILE *)0 : fopen(acSearch, "r");
  965. X            } while ((FILE *)0 == fin && (char *)0 != pchSecond);
  966. X            if (debug && (FILE *)0 != fin) {
  967. X                fprintf(stderr, "%s: searching template %s\n", progname, acSearch);
  968. X            }
  969. X            continue;
  970. X        }
  971. X        if ((char *)0 == pch) {
  972. X            break;
  973. X        }
  974. X        if ((char *)0 == translit(combuf, pch, 0)) {
  975. X            continue;
  976. X        }
  977. X        fFoundOne = 1;
  978. X        if (fConfirm) {
  979. X            fprintf(stderr, "\t%s  [fnyq]? ", combuf);
  980. X            fflush(stderr);
  981. X            gets(buf);
  982. X
  983. X            for (pch = buf; '\000' != *pch && isspace(*pch); ++pch)
  984. X                /* empty */;
  985. X            switch (*pch) {
  986. X            case 'q':
  987. X            case 'Q':
  988. X                exit(0);
  989. X            case 'y':
  990. X            case 'Y':
  991. X                break;
  992. X            case 'f':
  993. X            case 'F':
  994. X            default:
  995. X                continue;
  996. X            case 'n':
  997. X            case 'N':
  998. X                if ((FILE *)0 != fin)
  999. X                    fclose(fin);
  1000. X                return 0;
  1001. X            }
  1002. X        } else if (fVerbose) {
  1003. X            fprintf(stderr, "\t%s\n", combuf);
  1004. X        }
  1005. X        fflush(stderr);
  1006. X        if (fExec) {
  1007. X            auto int cur, code;
  1008. #if RESOURCE
  1009. X            code = rlimsys(combuf);
  1010. #else /* use vanilla system */
  1011. X            code = system(combuf);
  1012. X            if (0177 == (code & 0xff)) {        /* stopped */
  1013. X                code = (code >> 8) & 0xff;
  1014. X            } else if (0 != (code & 0xff)) {    /* killed */
  1015. X                code = code & 0x7f;
  1016. X            } else {                /* exit */
  1017. X                code = (code >> 8) & 0xff;
  1018. X            }
  1019. #endif /* check for resource limits */
  1020. X            if (debug)
  1021. X                fprintf(stderr, "%s: command exits %d\n", progname, code);
  1022. X            switch (exitcode.ekey) {
  1023. X            case EXIT_EXACT:
  1024. X                cur = code != exitcode.ivalue;
  1025. X                break;
  1026. X            case EXIT_EXACT|EXIT_NOT:
  1027. X                cur = code == exitcode.ivalue;
  1028. X                break;
  1029. X            case EXIT_ANY:
  1030. X                cur = 0;
  1031. X                break;
  1032. X            case EXIT_ANY|EXIT_NOT:
  1033. X                cur = 1;
  1034. X                break;
  1035. X            }
  1036. X            if (fFirst && 0 == cur) {
  1037. X                if ((FILE *)0 != fin)
  1038. X                    fclose(fin);
  1039. X                return 0;
  1040. X            }
  1041. X            count += cur;
  1042. X        }
  1043. X        if (fAll || fFirst) {
  1044. X            continue;
  1045. X        }
  1046. X        if ((FILE *)0 != fin) {
  1047. X            fclose(fin);
  1048. X        }
  1049. X        return count;
  1050. X    } while ((FILE *)0 != fin);
  1051. X    if (fVerbose && !fFoundOne) {
  1052. X        fprintf(stderr, "%s: no marker \"%c%s", progname, LEADCHAR, markstr);
  1053. X        if ((char *)0 != submark) {
  1054. X            fprintf(stderr, "(%s)", submark);
  1055. X        }
  1056. X        fprintf(stderr, ":\" %s %s\n", fFoundOne ? "selected from" : "in", curfile);
  1057. X    }
  1058. X    if (!fFoundOne)
  1059. X        ++count;
  1060. X    return count;
  1061. }
  1062. Purdue
  1063. chmod 0444 mk/mk.c ||
  1064. echo 'restore of mk/mk.c failed'
  1065. Wc_c="`wc -c < 'mk/mk.c'`"
  1066. test 21421 -eq "$Wc_c" ||
  1067.     echo 'mk/mk.c: original size 21421, current size' "$Wc_c"
  1068. fi
  1069. # ============= mk/Makefile ==============
  1070. if test -f 'mk/Makefile' -a X"$1" != X"-c"; then
  1071.     echo 'x - skipping mk/Makefile (File already exists)'
  1072. else
  1073. echo 'x - extracting mk/Makefile (Text)'
  1074. sed 's/^X//' << 'Purdue' > 'mk/Makefile' &&
  1075. # Written by S. McGeady, Intel, Inc., mcg@mipon2.intel.com        (sm)
  1076. #         Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb        (ksb)
  1077. #
  1078. # This software is not subject to any license of the American Telephone
  1079. # and Telegraph Company or the Regents of the University of California.
  1080. #
  1081. # Permission is granted to anyone to use this software for any purpose on
  1082. # any computer system, and to alter it and redistribute it freely, subject
  1083. # to the following restrictions:
  1084. #
  1085. # 1. The authors are not held responsible for any consequences of the
  1086. #    use of this software.
  1087. #
  1088. # 2. The origin of this software must not be misrepresented, either by
  1089. #    explicit claim or by omission.  Credit to the authors must appear
  1090. #    in documentation and sources.
  1091. #
  1092. # 3. Altered versions must be plainly marked as such, and must not be
  1093. #    misrepresented as being the original software.
  1094. #
  1095. # 4. This notice may not be removed or altered.
  1096. #
  1097. # Makefile for mk
  1098. #
  1099. # $Id: Makefile.plain,v 4.6 90/11/19 13:57:12 ksb Exp $
  1100. X
  1101. PROG=    mk
  1102. BIN=    ${DESTDIR}/usr/local/bin
  1103. X
  1104. L=../libopt
  1105. #L=/usr/include/local
  1106. I=/usr/include
  1107. S=/usr/include/sys
  1108. P=
  1109. X
  1110. INCLUDE= -I$L
  1111. DEBUG=    -O
  1112. CDEFS=     
  1113. CFLAGS= ${DEBUG} ${CDEFS} ${INCLUDE}
  1114. X
  1115. HDR=    main.h mk.h optaccum.h machine.h rlimsys.h
  1116. SRC=    main.c mk.c rcsname.c setenv.c optaccum.c rlimsys.c
  1117. GENc=
  1118. GENh=
  1119. GEN=    ${GENh} ${GENc}
  1120. DEP=    ${SRC} ${GENc}
  1121. OBJ=    main.o mk.o rcsname.o setenv.o optaccum.o rlimsys.o
  1122. MAN=    mk.1l
  1123. SOURCE=    Makefile Makefile.mkcmd README mk.m ${MAN} ${HDR} ${SRC}
  1124. X
  1125. all: ${PROG}
  1126. X
  1127. ${PROG}:$P ${OBJ}
  1128. #    ${CC} -o $@ ${CFLAGS} ${OBJ} -lopt
  1129. #    ${CC} -o $@ ${CFLAGS} ${OBJ} -L /usr/local/lib -lopt
  1130. X    ${CC} -o $@ ${CFLAGS} ${OBJ} ../libopt/libopt.a
  1131. X
  1132. self-test: ${PROG}
  1133. X    cd Tests; ../${PROG} -mCompile *
  1134. X
  1135. swap: ${HDR} ${SRC} ${GEN} Makefile.mkcmd
  1136. X    mkcmd std_help.m mk.m
  1137. X    -(cmp -s prog.c main.c || echo main.c changed)
  1138. X    -(cmp -s prog.h main.h || echo main.h changed)
  1139. X    mv Makefile Makefile.plain
  1140. X    mv Makefile.mkcmd Makefile
  1141. X
  1142. clean: FRC
  1143. X    rm -f Makefile.bak *.o prog.[ch] ${GEN} ${PROG} a.out core errs tags
  1144. X
  1145. depend: ${HDR} ${SRC} ${GEN} FRC
  1146. X    maketd ${CDEFS} ${INCLUDE} ${DEP}
  1147. X
  1148. install: all FRC
  1149. X    install -cs ${PROG} ${BIN}/${PROG}
  1150. X
  1151. lint: ${HDR} ${SRC} ${GEN} FRC
  1152. X    lint -h ${CDEFS} ${INCLUDE} ${DEP}
  1153. X
  1154. mkcat: ${MAN}
  1155. X    mkcat ${MAN}
  1156. X
  1157. print: source FRC
  1158. X    lpr -J'${PROG} source' ${SOURCE}
  1159. X
  1160. source: ${SOURCE}
  1161. X
  1162. spotless: clean
  1163. X    rcsclean ${SOURCE}
  1164. X
  1165. tags: ${HDR} ${SRC} ${GEN}
  1166. X    ctags -t ${HDR} ${SRC} ${GEN}
  1167. X
  1168. ${SOURCE}:
  1169. X    co -q $@
  1170. X
  1171. FRC:
  1172. X
  1173. # DO NOT DELETE THIS LINE - maketd DEPENDS ON IT
  1174. X
  1175. main.o: machine.h main.c optaccum.h
  1176. X
  1177. mk.o: machine.h main.h mk.c mk.h rlimsys.h
  1178. X
  1179. rcsname.o: machine.h rcsname.c
  1180. X
  1181. setenv.o: machine.h setenv.c
  1182. X
  1183. optaccum.o: machine.h optaccum.c
  1184. X
  1185. rlimsys.o: machine.h mk.h rlimsys.c rlimsys.h
  1186. X
  1187. # *** Do not add anything here - It will go away. ***
  1188. Purdue
  1189. chmod 0644 mk/Makefile ||
  1190. echo 'restore of mk/Makefile failed'
  1191. Wc_c="`wc -c < 'mk/Makefile'`"
  1192. test 2737 -eq "$Wc_c" ||
  1193.     echo 'mk/Makefile: original size 2737, current size' "$Wc_c"
  1194. fi
  1195. # ============= mk/Tests/ExitCodes ==============
  1196. if test ! -d 'mk/Tests'; then
  1197.     echo 'x - creating directory mk/Tests'
  1198.     mkdir 'mk/Tests'
  1199. fi
  1200. if test -f 'mk/Tests/ExitCodes' -a X"$1" != X"-c"; then
  1201.     echo 'x - skipping mk/Tests/ExitCodes (File already exists)'
  1202. else
  1203. echo 'x - extracting mk/Tests/ExitCodes (Text)'
  1204. sed 's/^X//' << 'Purdue' > 'mk/Tests/ExitCodes' &&
  1205. # This file checks mk's exit code traps                (ksb)
  1206. # $Id$
  1207. X
  1208. mk -s $0
  1209. exit $?
  1210. X
  1211. $Compile:\nfor group in True False Any None Number Sig;\ndo\n\t%b %v -m$group %f || (echo failed $group );\ndone\necho OK
  1212. X
  1213. X
  1214. These tests check for the different ways to say `zero is a good value'
  1215. X
  1216. $True=0: \n%b -a%V -t/dev/null -mOK %f
  1217. $OK(1)   : :
  1218. $OK(2)=0 : :
  1219. $OK(3)=1 : exit 1
  1220. $OK(4)=~0: exit 1
  1221. $OK(5)=~1: :
  1222. $OK(6)   : :
  1223. $OK(7)=0 : :
  1224. $OK(8)=~1: :
  1225. X
  1226. These thests check for the different fail under `zero is a good value'
  1227. $False=7: \n%b -a%V -t/dev/null -mBAD %f
  1228. $BAD(1)   : exit 1
  1229. $BAD(2)=0 : exit 1
  1230. $BAD(3)=1 : :
  1231. $BAD(4)=~0: :
  1232. $BAD(5)=~1: exit 1
  1233. $BAD(6)=1 : exit 0
  1234. $BAD(7)=~0: exit 0
  1235. X
  1236. These tests must all always succeed
  1237. $Any=0: \n%b -a%V -t/dev/null -mANY %f
  1238. $ANY(1)=*: :
  1239. $ANY(2)=*: exit 0
  1240. $ANY(3)=*: exit 1
  1241. $ANY(4)=*: exit 5
  1242. X
  1243. These tests should all always fail
  1244. $None=4: \n%b -a%V -t/dev/null -mNONE %f
  1245. $NONE(1)=~*: :
  1246. $NONE(2)=~*: exit 0
  1247. $NONE(3)=~*: exit 1
  1248. $NONE(4)=~*: exit 5
  1249. X
  1250. $Number=0: \n%b -a%V -t/dev/null -mVALUE %f
  1251. $VALUE(0)=0: exit 0
  1252. $VALUE(1)=1: exit 1
  1253. $VALUE(2)=2: exit 2
  1254. $VALUE(3)=3: exit 3
  1255. $VALUE(4)=4: exit 4
  1256. X
  1257. $Sig=0: \n%b -a%V -t/dev/null -mSIG %f
  1258. $SIG=1: kill -1 \$$
  1259. $SIG=15: kill -15 \$$
  1260. Purdue
  1261. chmod 0644 mk/Tests/ExitCodes ||
  1262. echo 'restore of mk/Tests/ExitCodes failed'
  1263. Wc_c="`wc -c < 'mk/Tests/ExitCodes'`"
  1264. test 1182 -eq "$Wc_c" ||
  1265.     echo 'mk/Tests/ExitCodes: original size 1182, current size' "$Wc_c"
  1266. fi
  1267. # ============= mk/Tests/compat ==============
  1268. if test -f 'mk/Tests/compat' -a X"$1" != X"-c"; then
  1269.     echo 'x - skipping mk/Tests/compat (File already exists)'
  1270. else
  1271. echo 'x - extracting mk/Tests/compat (Text)'
  1272. sed 's/^X//' << 'Purdue' > 'mk/Tests/compat' &&
  1273. $Compile:  MK=-t/dev/null %b -a%C%I%N%V -mVars -Dfalse="exit 0" %f && echo Vars test OK
  1274. X
  1275. # the OLD mk used to expand shell variable and some other strange
  1276. # command line variables.  These have been passed off to the shell
  1277. # to do.  This test makes sure OLD style calls get converted into
  1278. # shell variable substitutions correctly.  (ksb)
  1279. X
  1280. $Vars(1): %`false`
  1281. $Vars(2): %{false}
  1282. Purdue
  1283. chmod 0644 mk/Tests/compat ||
  1284. echo 'restore of mk/Tests/compat failed'
  1285. Wc_c="`wc -c < 'mk/Tests/compat'`"
  1286. test 376 -eq "$Wc_c" ||
  1287.     echo 'mk/Tests/compat: original size 376, current size' "$Wc_c"
  1288. fi
  1289. # ============= mk/rlimsys.h ==============
  1290. if test -f 'mk/rlimsys.h' -a X"$1" != X"-c"; then
  1291.     echo 'x - skipping mk/rlimsys.h (File already exists)'
  1292. else
  1293. echo 'x - extracting mk/rlimsys.h (Text)'
  1294. sed 's/^X//' << 'Purdue' > 'mk/rlimsys.h' &&
  1295. /*
  1296. X * include file for rlimited system
  1297. X */
  1298. X
  1299. #if RESOURCE
  1300. extern int rlimsys();
  1301. extern char *rparse();
  1302. extern void rinit();
  1303. extern int r_fTrace;    /* use shell -cx for verbose trace        */
  1304. #endif
  1305. Purdue
  1306. chmod 0444 mk/rlimsys.h ||
  1307. echo 'restore of mk/rlimsys.h failed'
  1308. Wc_c="`wc -c < 'mk/rlimsys.h'`"
  1309. test 190 -eq "$Wc_c" ||
  1310.     echo 'mk/rlimsys.h: original size 190, current size' "$Wc_c"
  1311. fi
  1312. # ============= mkcat/scan.c ==============
  1313. if test ! -d 'mkcat'; then
  1314.     echo 'x - creating directory mkcat'
  1315.     mkdir 'mkcat'
  1316. fi
  1317. if test -f 'mkcat/scan.c' -a X"$1" != X"-c"; then
  1318.     echo 'x - skipping mkcat/scan.c (File already exists)'
  1319. else
  1320. echo 'x - extracting mkcat/scan.c (Text)'
  1321. sed 's/^X//' << 'Purdue' > 'mkcat/scan.c' &&
  1322. /*
  1323. X * Copyright 1990 Purdue Research Foundation, West Lafayette, Indiana
  1324. X * 47907.  All rights reserved.
  1325. X *
  1326. X * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb
  1327. X *
  1328. X * This software is not subject to any license of the American Telephone
  1329. X * and Telegraph Company or the Regents of the University of California.
  1330. X *
  1331. X * Permission is granted to anyone to use this software for any purpose on
  1332. X * any computer system, and to alter it and redistribute it freely, subject
  1333. X * to the following restrictions:
  1334. X *
  1335. X * 1. Neither the authors nor Purdue University are responsible for any
  1336. X *    consequences of the use of this software.
  1337. X *
  1338. X * 2. The origin of this software must not be misrepresented, either by
  1339. X *    explicit claim or by omission.  Credit to the authors and Purdue
  1340. X *    University must appear in documentation and sources.
  1341. X *
  1342. X * 3. Altered versions must be plainly marked as such, and must not be
  1343. X *    misrepresented as being the original software.
  1344. X *
  1345. X * 4. This notice may not be removed or altered.
  1346. X */
  1347. X
  1348. /*  $Id: scan.c,v 3.7 90/11/28 09:54:33 ksb Exp $
  1349. X *
  1350. X * scan -- look for trouble in the cat dirs (mostly)            (ksb)
  1351. X */
  1352. #include "machine.h"
  1353. X
  1354. #include <stdio.h>
  1355. #include <ctype.h>
  1356. #include <math.h>
  1357. #include <errno.h>
  1358. #include <sys/file.h>
  1359. #include <sys/types.h>
  1360. #include <sys/param.h>
  1361. #if defined(SYSV)
  1362. #include <ndir.h>
  1363. #else
  1364. #include <sys/dir.h>
  1365. #endif
  1366. #include <sys/stat.h>
  1367. #include <fcntl.h>
  1368. X
  1369. #if !defined(MAXPATHLEN)
  1370. #define MAXPATHLEN    1024
  1371. #endif
  1372. X
  1373. #include "main.h"
  1374. #include "pt.h"
  1375. #include "genwhatis.h"
  1376. #include "format.h"
  1377. #include "readman.h"
  1378. #include "mkcat.h"
  1379. X
  1380. extern FILE *popen();
  1381. extern long atol();
  1382. extern int errno;
  1383. extern char *sys_errlist[];
  1384. #define strerror(Me) sys_errlist[Me]
  1385. X
  1386. extern char *strrchr(), *strcat(), *strcpy(), *malloc(), *realloc();
  1387. X
  1388. #if !defined(F_OK)
  1389. #define F_OK    0
  1390. #endif
  1391. X
  1392. #if !defined(R_OK)
  1393. #define R_OK    4
  1394. #endif
  1395. X
  1396. X
  1397. /*
  1398. X * are these two files the same?                    (ksb)
  1399. X *    same inode
  1400. X *        return yes
  1401. X *    same compress status?
  1402. X *        return system("cmp -s file1 file2");
  1403. X *    diff compress status
  1404. X *        return system("zcat file.Z | cmp -s - file");
  1405. X */
  1406. int
  1407. CmpFile(pcFile1, pcFile2)
  1408. char *pcFile1, *pcFile2;
  1409. {
  1410. X    register char *pcComp1, *pcComp2;
  1411. X    auto struct stat stat1, stat2;
  1412. X    auto char acCmd[MAXPATHLEN*4];
  1413. X
  1414. X    if (-1 == stat(pcFile1, & stat1) || -1 == stat(pcFile2, & stat2)) {
  1415. X        return 0;
  1416. X    }
  1417. X    if (stat1.st_ino == stat2.st_ino && stat1.st_dev == stat2.st_dev) {
  1418. X        return 1;
  1419. X    }
  1420. X    pcComp1 = strrchr(pcFile1, acDotZ[0]);
  1421. X    if ((char *)0 != pcComp1 && 0 != strcmp(acDotZ, pcComp1)) {
  1422. X        pcComp1 = (char *)0;
  1423. X    }
  1424. X    pcComp2 = strrchr(pcFile2, acDotZ[0]);
  1425. X    if ((char *)0 != pcComp2 && 0 != strcmp(acDotZ, pcComp2)) {
  1426. X        pcComp2 = (char *)0;
  1427. X    }
  1428. X    if (((char *)0 != pcComp1) == ((char *)0 != pcComp2)) {
  1429. X        sprintf(acCmd, "exec cmp -s \'%s\' \'%s\'", pcFile1, pcFile2);
  1430. X    } else if ((char *)0 == pcComp2) {
  1431. X        sprintf(acCmd, "zcat \'%s\' |exec cmp -s - \'%s\'", pcFile1, pcFile2);
  1432. X    } else {
  1433. X        sprintf(acCmd, "zcat \'%s\' |exec cmp -s - \'%s\'", pcFile2, pcFile1);
  1434. X    }
  1435. X    return 0 == system(acCmd);
  1436. }
  1437. X
  1438. /*
  1439. X * if we find a compress'd file uncompress if (fCompress)        (ksb)
  1440. X * if we find an uncompress'd file coompress if (!fCompress)
  1441. X * make symboliclinks point to correct version.
  1442. X *
  1443. X * piNeed is a count of how many we would change
  1444. X */
  1445. static int
  1446. FixCompress(npg, ppDE, piNeed)
  1447. int npg, *piNeed;
  1448. struct direct **ppDE;
  1449. {
  1450. X    auto char acOther[MAXPATHLEN+1];
  1451. X    auto char acCmd[MAXPATHLEN*2+100];
  1452. X    auto char acLink[MAXPATHLEN+1];
  1453. X    struct direct *pDEThis;
  1454. X    register int i, j;
  1455. X    auto int iLinkLen;
  1456. X    auto PATH PTThis, PTLink;
  1457. X    auto struct stat stThis;
  1458. X
  1459. X    *piNeed = 0;
  1460. X    for (i = 0; i < npg; ++i) {
  1461. X        if ((struct direct *)0 == (pDEThis = ppDE[i]))
  1462. X            continue;
  1463. X        ppDE[i] = (struct direct *)0;
  1464. X        PTInit(&PTThis, pDEThis->d_name);
  1465. X        if (fCompress) {
  1466. X            if (PTIsComp(&PTThis))
  1467. X                continue;
  1468. X            (void)strcpy(acOther, PTFull(&PTThis));
  1469. X            (void)PTComp(&PTThis);
  1470. X        } else {
  1471. X            if (!PTIsComp(&PTThis))
  1472. X                continue;
  1473. X            (void)strcpy(acOther, PTFull(&PTThis));
  1474. X            (void)PTUnComp(&PTThis);
  1475. X        }
  1476. X
  1477. X        /* now Other has the wrong one, and PTThis is correct
  1478. X         */
  1479. X        if (-1 == LSTAT(acOther, & stThis)) {
  1480. X            /* someone removed it, ouch */
  1481. X            continue;
  1482. X        }
  1483. X        if (0 == access(PTFull(&PTThis), F_OK)) {
  1484. X            if (0 == CmpFile(PTFull(&PTThis), acOther)) {
  1485. X                if (fVerbose) {
  1486. X                    (void)printf("%s: rm -f %s\n", progname, acOther);
  1487. X                }
  1488. X                if (fExec && -1 == unlink(acOther)) {
  1489. X                    fprintf(stderr, "%s: unlink: %s: %s\n", progname, acOther, strerror(errno));
  1490. X                    exit(22);
  1491. X                }
  1492. X            } else {
  1493. X                (void)printf("%s: %s ~ %s, both exist; delete one and retry\n", progname, PTFull(&PTThis), acOther);
  1494. X                exit(88);
  1495. X            }
  1496. X        }
  1497. X        switch (stThis.st_mode & S_IFMT) {
  1498. X        case S_IFREG:
  1499. X            (void)sprintf(acCmd, "%scompress -f -c \"%s\" >\"%s\"", fCompress ? "" : "un", acOther, PTFull(&PTThis));
  1500. X            if (fVerbose)
  1501. X                printf("%s: %s\n", progname, acCmd);
  1502. X            if (fExec && 0 != system(acCmd))
  1503. X                continue;
  1504. X            ++*piNeed;
  1505. X            for (j = i+1; j < npg; ++j) {
  1506. X                if ((struct direct *)0 == ppDE[j])
  1507. X                    continue;
  1508. X                if (ppDE[j]->d_ino != pDEThis->d_ino)
  1509. X                    continue;
  1510. X                PTInit(&PTLink, ppDE[j]->d_name);
  1511. X                if (fVerbose)
  1512. X                    printf("%s: rm -f %s\n", progname, PTFull(&PTLink));
  1513. X                if (fExec)
  1514. X                    (void)unlink(PTFull(&PTLink));
  1515. X                if (fCompress)
  1516. X                    (void)PTComp(&PTLink);
  1517. X                else
  1518. X                    (void)PTUnComp(&PTLink);
  1519. X                if (fVerbose)
  1520. X                    printf("%s: ln %s %s\n", progname, PTFull(&PTThis), PTFull(&PTLink));
  1521. X                if (fExec && 0 != link(PTFull(&PTThis), PTFull(&PTLink))) {
  1522. X                    fprintf(stderr, "%s: link: %s: %s\n", progname, PTFull(&PTLink), strerror(errno));
  1523. X                }
  1524. X                ppDE[j] = (struct direct *)0;
  1525. X            }
  1526. X            if (fVerbose) {
  1527. X                printf("%s: rm -f %s\n", progname, acOther);
  1528. X            }
  1529. X            if (fExec && 0 != unlink(acOther)) {
  1530. X                fprintf(stderr, "%s: unlink: %s: %s\n", progname, acOther, strerror(errno));
  1531. X            }
  1532. X            break;
  1533. #if HAVE_SLINKS
  1534. X        case S_IFLNK:
  1535. X            if (-1 == (iLinkLen = readlink(acOther, acLink, MAXPATHLEN))) {
  1536. X                fprintf(stderr, "%s: readlink: %s: %s\n", progname, acOther, strerror(errno));
  1537. X                continue;
  1538. X            }
  1539. X            acLink[iLinkLen] = '\000';
  1540. X            printf("%s: %s -> %s\n", progname, acOther, acLink);
  1541. X            if ('/' == acLink[0])
  1542. X                continue;
  1543. X            PTInit(& PTLink, acLink);
  1544. X            if (fCompress)
  1545. X                (void)PTComp(&PTLink);
  1546. X            else
  1547. X                (void)PTUnComp(&PTLink);
  1548. X            if (fVerbose)
  1549. X                (void)printf("%s: rm -f \"%s\"\n", progname, acOther);
  1550. X            if (fExec && 0 != unlink(acOther))
  1551. X                fprintf(stderr, "%s: unlink: %s: %s\n", progname, acOther, strerror(errno));
  1552. X            if (fVerbose)
  1553. X                (void)printf("%s: ln -s \"%s\" \"%s\"\n", progname, PTFull(&PTLink), PTFull(&PTThis));
  1554. X            if (fExec && symlink(PTFull(&PTThis), PTFull(&PTLink)))
  1555. X                fprintf(stderr, "%s: link: %s\n", progname, strerror(errno));;
  1556. X            break;
  1557. #endif
  1558. X        case S_IFDIR:
  1559. X            /* OLD, for instance */
  1560. X            continue;
  1561. X        default:
  1562. X            (void)fprintf(stderr, "%s: %s: unknown file type\n", progname, PTFull(&PTThis));
  1563. X            continue;
  1564. X        }
  1565. X    }
  1566. X    return 0;
  1567. }
  1568. X
  1569. /*
  1570. X * In this alphasorted list of (struct direct *) [some nil] find    (ksb)
  1571. X * (via a binary search) the named file... return a pointer to
  1572. X * the entry so we may remove it in the caller
  1573. X *
  1574. X * This allows the caller to `shoot' the return array from scandir
  1575. X * a few files at a time and still have a list of the files left.
  1576. X */
  1577. static struct direct **
  1578. FindDEnt(pcName, ppDE, n)
  1579. char *pcName;
  1580. struct direct **ppDE;
  1581. int n;
  1582. {
  1583. X    register int i, mid;
  1584. X    register int cmp;
  1585. X
  1586. X    if (0 == n)
  1587. X        return (struct direct **)0;
  1588. X    for (i = 0; i < n; ++i) {
  1589. X        if ((struct direct *)0 != ppDE[i])
  1590. X            break;
  1591. X    }
  1592. X    if (n == i) {
  1593. X        return (struct direct **)0;
  1594. X    }
  1595. X
  1596. X    if (0 == strcmp(pcName, (ppDE[i])->d_name)) {
  1597. X        return & ppDE[i];
  1598. X    }
  1599. X
  1600. X    do {
  1601. X        --n;
  1602. X        if ((struct direct *)0 != ppDE[n])
  1603. X            break;
  1604. X    } while (n > i);
  1605. X    if (0 == strcmp(pcName, (ppDE[n])->d_name)) {
  1606. X        return & ppDE[n];
  1607. X    }
  1608. X
  1609. X    while (i != n) {
  1610. X        mid = (n+i)/2;
  1611. X        while (mid < n && (struct direct *)0 == ppDE[mid])
  1612. X            ++mid;
  1613. X        if (mid == n) {
  1614. X            mid = (n+i)/2;
  1615. X            while (mid > i && (struct direct *)0 == ppDE[mid])
  1616. X                --mid;
  1617. X            if (mid == i)
  1618. X                break;
  1619. X        }
  1620. X        cmp = strcmp(pcName, (ppDE[mid])->d_name);
  1621. X        if (cmp == 0) {
  1622. X            return & ppDE[mid];
  1623. X        }
  1624. X        if (cmp < 0) {
  1625. X            n = mid;
  1626. X        } else {
  1627. X            i = mid;
  1628. X        }
  1629. X        if (i+1 == n)
  1630. X            break;
  1631. X    }
  1632. X    return (struct direct **)0;
  1633. }
  1634. X
  1635. /*
  1636. X * remove a link entry from something                    (ksb)
  1637. X *     1 == OK to build
  1638. X *     0 == done for us already
  1639. X *    -1 == error on rm
  1640. X *     2 == just struke link from list, fMkLinks is not set!
  1641. X */
  1642. static int
  1643. FixLink(pcEnt, ppDEPages, npg, pcDest, pstCat)
  1644. char *pcEnt, *pcDest;    /* ZZZ where the link points */
  1645. struct direct **ppDEPages;
  1646. int npg;
  1647. struct stat *pstCat;
  1648. {
  1649. X    register struct direct **ppDE;
  1650. X    int iLinkLen;
  1651. X    auto char acContents[MAXPATHLEN+1];
  1652. X    auto struct stat stDest;
  1653. X
  1654. X    if ((struct direct **)0 != ppDEPages && (struct direct **)0 != (ppDE = FindDEnt(pcEnt, ppDEPages, npg))) {
  1655. X        *ppDE = (struct direct *)0;
  1656. X    }
  1657. X    if (!fMkLinks)
  1658. X        return 2;
  1659. X
  1660. X    if (-1 == LSTAT(pcEnt, & stDest)) {
  1661. X        if (ENOENT != errno) {
  1662. X            fprintf(stderr, "%s: stat: %s: %s\n", progname, pcEnt, strerror(errno));
  1663. X            return -1;
  1664. X        }
  1665. X        return 1;
  1666. X    }
  1667. X    switch (stDest.st_mode & S_IFMT) {
  1668. X    case 0:
  1669. X    case S_IFREG:    /* regular */
  1670. X        if (fUseHards) {
  1671. X            if ((struct stat *)0 != pstCat && stDest.st_ino == pstCat->st_ino && stDest.st_dev == pstCat->st_dev) {
  1672. X                /* OK */
  1673. X                return 0;
  1674. X            }
  1675. X            if (stDest.st_nlink < 2) {
  1676. X                fprintf(fpOut, "%s: `%s\' is a unique file, not removed\n", progname, pcEnt);
  1677. X                return -1;
  1678. X            }
  1679. X            fprintf(fpOut, "%s: hard moved: %s@ -> %s\n", progname, pcEnt, pcDest);
  1680. X        } else {
  1681. X            /* if this is not the old cat page, and
  1682. X             * a unique file that is not a clone of the page
  1683. X             * remove it.
  1684. X             */
  1685. X            if ((struct stat *)0 != pstCat && (stDest.st_ino == pstCat->st_ino && stDest.st_dev == pstCat->st_dev)) {
  1686. X                /* it is an OK hard link, dink it */;
  1687. X            } else if (stDest.st_nlink < 2 && 0 != CmpFile(pcEnt, acCat)) {
  1688. X                fprintf(fpOut, "%s: `%s\' is a unique file, not removed\n", progname, pcEnt);
  1689. X                return -1;
  1690. X            }
  1691. X            fprintf(fpOut, "%s: hard link changed to symbolic link: %s@ -> %s\n", progname, pcEnt, pcDest);
  1692. X        }
  1693. X        break;
  1694. X
  1695. #if HAVE_SLINKS
  1696. X    case S_IFLNK:    /* symbolic link */
  1697. X        if (-1 == (iLinkLen = readlink(pcEnt, acContents, MAXPATHLEN))) {
  1698. X            fprintf(stderr, "%s: readlink: %s: %s\n", progname, pcEnt, strerror(errno));
  1699. X            return -1;
  1700. X        }
  1701. X        acContents[iLinkLen] = '\000';
  1702. X        if (fUseHards) {
  1703. X            if (0 != strcmp(acContents, pcDest) && 0 != CmpFile(pcEnt, acContents)) {
  1704. X                return -1;
  1705. X            }
  1706. X            fprintf(fpOut, "%s: symbolic link changed to a hard link: %s -> %s\n", progname, pcEnt, pcDest);
  1707. X        } else {
  1708. X            if (0 == strcmp(acContents, pcDest)) {
  1709. X                return 0;
  1710. X            }
  1711. X            fprintf(fpOut, "%s: symbolic link moved: %s@ -> %s\n", progname, pcEnt, pcDest);
  1712. X        }
  1713. X        break;
  1714. X    default:
  1715. X        fprintf(fpOut, "%s: `%s\' is a special file, cannot change to a symlink\n", progname, pcEnt);
  1716. X        return -1;
  1717. X
  1718. #else
  1719. X    default:
  1720. X        fprintf(fpOut, "%s: `%s\' is a special file, cannot change to a link\n", progname, pcEnt);
  1721. X        return -1;
  1722. #endif
  1723. X    }
  1724. X    /* link was ok to remove and pointed the wrong place
  1725. X     */
  1726. X    if (fVerbose) {
  1727. X        fprintf(fpOut, "%s: rm -f \"%s\"\n", progname, pcEnt);
  1728. X    }
  1729. X    if (fExec) {
  1730. X        (void)unlink(pcEnt);
  1731. X    }
  1732. X    return 1;
  1733. }
  1734. X
  1735. /*
  1736. X * update all the links that point to this page                (ksb)
  1737. X *
  1738. X * We chdir() to the target dir and chdir back when done.
  1739. X *
  1740. X * Passed a list of links to make, a file to link them to,
  1741. X * the stat buffer for an `OK' previous file that links may
  1742. X * have been hard linked to, and a list of files in that directory
  1743. X * that we should `punch out' as they are claimed as links
  1744. X * (so we don't have to do this again).
  1745. X *
  1746. X * Optionally, remove the links...
  1747. X */
  1748. static void
  1749. MkLinks(pcBase, pWU, ppDEPages, npg, pstCat, fRemove)
  1750. WHATIS *pWU;
  1751. char *pcBase;
  1752. struct direct **ppDEPages;
  1753. int npg, fRemove;
  1754. struct stat *pstCat;
  1755. {
  1756. X    extern char *getwd();
  1757. X    register int k;
  1758. X    auto char acCLink[MAXPATHLEN+1];
  1759. X
  1760. X    for (k = 0; k < pWU->ilen; ++k) {
  1761. X        /* do not link file to itself */
  1762. X        (void)sprintf(acCLink, "%s.%s%s", pWU->ppclist[k], pWU->pcext, fCompress ? acDotZ : "");
  1763. X        if (0 == strcmp(pcBase, acCLink)) {
  1764. X            continue;
  1765. X        }
  1766. X
  1767. X        switch (FixLink(acCLink, ppDEPages, npg, pcBase, pstCat)) {
  1768. X        case 1:    /* make it -- it is not there            */
  1769. X            if (fRemove) {
  1770. X                continue;
  1771. X            }
  1772. X            /* make it below */
  1773. X            break;
  1774. X
  1775. X        case 0: /* it is there already                */
  1776. X            if (!fRemove) {
  1777. X                continue;
  1778. X            }
  1779. X            if (fVerbose) {
  1780. X                fprintf(fpOut, "%s: rm -f \"%s\"\n", progname, acCLink);
  1781. X            }
  1782. X            if (fExec) {
  1783. X                (void)unlink(acCLink);
  1784. X            }
  1785. X            continue;
  1786. X
  1787. X        case -1: /* error on remove/check of link -- forget it    */
  1788. X            continue;
  1789. X        case 2:    /* fMkLinks is 0 */
  1790. X            continue;
  1791. X        }
  1792. X
  1793. X        /* make a link
  1794. X         */
  1795. X        if (fUseHards) {
  1796. X            if (fVerbose) {
  1797. X                fprintf(fpOut, "%s: ln \"%s\" \"%s\"\n", progname, pcBase, acCLink);
  1798. X            }
  1799. X            if (fExec && 0 != link(pcBase, acCLink)) {
  1800. X                fprintf(stderr, "%s: link: %s to %s: %s\n", progname, pcBase, acCLink, strerror(errno));
  1801. X                continue;
  1802. X            }
  1803. X        } else {
  1804. X            if (fVerbose) {
  1805. X                fprintf(fpOut, "%s: ln -s \"%s\" \"%s\"\n", progname, pcBase, acCLink);
  1806. X            }
  1807. X            if (fExec && 0 != symlink(pcBase, acCLink)) {
  1808. X                fprintf(stderr, "%s: symlink: %s to %s: %s\n", progname, pcBase, acCLink, strerror(errno));
  1809. X                continue;
  1810. X            }
  1811. X        }
  1812. X    }
  1813. }
  1814. X
  1815. /*
  1816. X * select routine for scandir                        (ksb)
  1817. X */
  1818. static int
  1819. catSelect(pEnt)
  1820. struct direct *pEnt;
  1821. {
  1822. X    if ('.' == pEnt->d_name[0] && ('\000' == pEnt->d_name[1] ||
  1823. X        ('.' == pEnt->d_name[1] && '\000' == pEnt->d_name[2]))) {
  1824. X        return 0;
  1825. X    }
  1826. X    return 1;
  1827. }
  1828. X
  1829. /*
  1830. X * scan the read directory for pages that differ only            (ksb)
  1831. X * in extender or case
  1832. X */
  1833. static void
  1834. ScanDups(npg, ppDEPages, pWU)
  1835. int npg;
  1836. register struct direct **ppDEPages;
  1837. register WHATIS *pWU;
  1838. {
  1839. X    register int k, l;
  1840. X    register int ck, cl;
  1841. X    register char *pcCompk, *pcCompl;
  1842. X    register char *pcSectionk, *pcSectionl;
  1843. X
  1844. X    for (k = 0; k < npg; ++k) {
  1845. X        pWU[k].isection = 1;
  1846. X    }
  1847. X
  1848. X    pcSectionk = pcCompk = (char *)0;
  1849. X    pcSectionl = pcCompl = (char *)0;
  1850. X    for (k = 0; k < npg;
  1851. X         ((char *)0 != pcCompk && (*pcCompk = acDotZ[0])),
  1852. X         ((char *)0 != pcSectionk && (*pcSectionk = '.')),
  1853. X         (pWU[k++].isection = 0)) {
  1854. X        if ((struct direct *)0 == ppDEPages[k]) {
  1855. X            continue;
  1856. X        }
  1857. X        pcCompk = strrchr(ppDEPages[k]->d_name, acDotZ[0]);
  1858. X        if ((char *)0 != pcCompk && 0 != strcmp(pcCompk, acDotZ)) {
  1859. X            pcCompk = (char *)0;
  1860. X        }
  1861. X        if ((char *)0 != pcCompk) {
  1862. X            *pcCompk = '\000';
  1863. X        }
  1864. X        pcSectionk = strrchr(ppDEPages[k]->d_name, '.');
  1865. X        if ((char *)0 == pcSectionk) {
  1866. X            continue;
  1867. X        }
  1868. X        *pcSectionk = '\000';
  1869. X        if (IsOKBase(ppDEPages[k]->d_name)) {
  1870. X            continue;
  1871. X        }
  1872. X        ck = ppDEPages[k]->d_name[0];
  1873. X        if (isalpha(ck) && isupper(ck)) {
  1874. X            ck = tolower(ck);
  1875. X        }
  1876. X
  1877. X        for (l = k+1; l < npg;
  1878. X             ((char *)0 != pcCompl && (*pcCompl = acDotZ[0])),
  1879. X             ((char *)0 != pcSectionl && (*pcSectionl = '.')),
  1880. X             ++l) {
  1881. X            if ((struct direct *)0 == ppDEPages[l] || 0 == pWU[l].isection)
  1882. X                continue;
  1883. X            cl = ppDEPages[l]->d_name[0];
  1884. X            if (isalpha(cl) && isupper(cl))
  1885. X                cl = tolower(cl);
  1886. X            if (cl != ck) {
  1887. X                continue;
  1888. X            }
  1889. X            pcCompl = strrchr(ppDEPages[l]->d_name, acDotZ[0]);
  1890. X            if ((char *)0 != pcCompl && 0 != strcmp(pcCompl, acDotZ)) {
  1891. X                pcCompl = (char *)0;
  1892. X            }
  1893. X            if ((char *)0 != pcCompl) {
  1894. X                *pcCompl = '\000';
  1895. X            }
  1896. X            pcSectionl = strrchr(ppDEPages[l]->d_name, '.');
  1897. X            if ((char *)0 == pcSectionl) {
  1898. X                continue;
  1899. X            }
  1900. X            *pcSectionl = '\000';
  1901. X            if (0 != strcasecmp(ppDEPages[k]->d_name, ppDEPages[l]->d_name)) {
  1902. X                continue;
  1903. X            }
  1904. X            fprintf(fpOut, "%s: `%s.%s%s\' and `%s.%s%s\' differ ", progname,
  1905. X                ppDEPages[k]->d_name, pcSectionk+1, (char *)0 != pcCompk ? acDotZ : "",
  1906. X                ppDEPages[l]->d_name, pcSectionl+1, (char *)0 != pcCompl ? acDotZ : "");
  1907. X            pWU[l].isection = 0;
  1908. X            if (0 == strcmp(ppDEPages[k]->d_name, ppDEPages[l]->d_name) && 0 == strcmp(pcSectionk+1, pcSectionl+1)) {
  1909. X                fprintf(fpOut, "only in compression\n");
  1910. X            } else if (0 == strcasecmp(pcSectionk+1, pcSectionl+1)) {
  1911. X                if (((char *)0 == pcCompl) == ((char *)0 == pcCompk)) {
  1912. X                    fprintf(fpOut, "only in case\n");
  1913. X                } else {
  1914. X                    fprintf(fpOut, "in case and compression\n");
  1915. X                }
  1916. X            } else if (((char *)0 == pcCompl) == ((char *)0 == pcCompk)) {
  1917. X                fprintf(fpOut, "only in section\n");
  1918. X            } else {
  1919. X                fprintf(fpOut, "in section and compression\n");
  1920. X            }
  1921. X        }
  1922. X    }
  1923. }
  1924. X
  1925. /* remove the case from a file name, but don't touch the acDotZ if    (ksb)
  1926. X * it has one
  1927. X */
  1928. void
  1929. DropCase(pc, fComp)
  1930. char *pc;
  1931. int fComp;
  1932. {
  1933. X    for (/* done*/; '\000' != *pc; ++pc) {
  1934. X        if (fComp && *pc == acDotZ[0] && 0 == strcmp(acDotZ, pc))
  1935. X            break;
  1936. X        if (isalpha(*pc) && isupper(*pc))
  1937. X            *pc = tolower(*pc);
  1938. X    }
  1939. }
  1940. X
  1941. /*
  1942. X * check a SEE ALSO entry for format, then check the manual page    (ksb)
  1943. X * it points to (if the format is correct).
  1944. X */
  1945. void
  1946. AlsoCheck(pWUThis, pcSee)
  1947. WHATIS *pWUThis;
  1948. char *pcSee;
  1949. {
  1950. X    register char *pcOpen, *pcClose, *pcTail, *pcDot;
  1951. X    register DIR *pDI;
  1952. X    register struct direct *pDE;
  1953. X    register int iBase;
  1954. X    extern char *strchr();
  1955. X    auto struct stat stPage;
  1956. X    auto char acLookFor[2*MAXPATHLEN+4];
  1957. X    auto char acWrong[MAXPATHLEN+1];
  1958. X
  1959. X    if ((char *)0 == (pcOpen = strchr(pcSee, '('/*)*/)) || pcSee == pcOpen || !isdigit(pcOpen[1]))
  1960. X        return;
  1961. X    if ((char *)0 == (pcClose = strchr(pcSee, /*(*/')')) || '\000' != pcClose[1])
  1962. X        return;
  1963. X    *pcOpen++ = '\000';
  1964. X    *pcClose = '\000';
  1965. X
  1966. X    if ('/' == pcCat[0] || ('.' == pcCat[0] && '/' == pcCat[1]))
  1967. X        sprintf(acLookFor, "%s%ld/", pcCat, atol(pcOpen));
  1968. X    else
  1969. X        sprintf(acLookFor, "%s/%s%ld/", pcRoot, pcCat, atol(pcOpen));
  1970. X    StripFmt(acLookFor);
  1971. X    pcTail = acLookFor + strlen(acLookFor);
  1972. X
  1973. X    sprintf(pcTail, "%s.%s", pcSee, pcOpen);
  1974. X    StripFmt(pcTail);
  1975. X    if ((char *)0 == (pcDot = strrchr(pcTail, '.'))) {
  1976. X        fprintf(stderr, "%s: %s: no dot in this string?\n", progname, pcTail);
  1977. X        exit(60);
  1978. X    }
  1979. X    iBase = pcDot - pcTail;
  1980. X    if (fCompress) {
  1981. X        (void)strcat(pcTail, acDotZ);
  1982. X    }
  1983. X
  1984. X    if (-1 != stat(acLookFor, &stPage) && S_IFREG == (stPage.st_mode&S_IFMT)) {
  1985. X        /* found it */;
  1986. X    } else if (DropCase(pcTail, fCompress), -1 != stat(acLookFor, &stPage) && S_IFREG == (stPage.st_mode&S_IFMT)) {
  1987. X        /* found it */;
  1988. X    } else if (pcTail[-1] = '\000', (DIR *)0 == (pDI = opendir(acLookFor))) {
  1989. X        fprintf(stderr, "%s: opendir: %s: %s\n", progname, acLookFor, strerror(errno));
  1990. X    } else {
  1991. X        /* printf("\tscan for %s in %s:\n", pcTail, acLookFor); */
  1992. X        /* if the name appears in any case we quit,
  1993. X         * if it has the wrong section we record it,
  1994. X         */
  1995. X        if (fCompress && (char *)0 != (pcDot = strrchr(pcTail, acDotZ[0])) && 0 == strcmp(acDotZ, pcDot)) {
  1996. X            *pcDot = '\000';
  1997. X        }
  1998. X        acWrong[0] = '\000';
  1999. X        while ((struct direct *)0 != (pDE = readdir(pDI))) {
  2000. X            if ((char *)0 != (pcDot = strrchr(pDE->d_name, acDotZ[0])) && 0 == strcmp(acDotZ, pcDot))
  2001. X                *pcDot = '\000';
  2002. X            else
  2003. X                pcDot = (char *)0;
  2004. X            if (0 == strcasecmp(pcTail, pDE->d_name)) {
  2005. X                break;
  2006. X            }
  2007. X            if (0 == strncasecmp(pcTail, pDE->d_name, iBase) && '.' == pDE->d_name[iBase]) {
  2008. X                (void)strcpy(acWrong, pDE->d_name);
  2009. X            }
  2010. X        }
  2011. X        closedir(pDI);
  2012. X        if ((struct direct *)0 != pDE) {
  2013. X            /* found it*/ ;
  2014. X        } else if ('\000' != acWrong[0]) {
  2015. X            printf("%s: %s.%s: SEE ALSO sites `%s\' found `%s\'\n", progname, pWUThis->pcbase, pWUThis->pcext, pcTail, acWrong);
  2016. X        
  2017. X        } else {
  2018. X            printf("%s: %s.%s: SEE ALSO sites `%s\', no such page\n", progname, pWUThis->pcbase, pWUThis->pcext, pcTail);
  2019. X        }
  2020. X    }
  2021. X
  2022. X    *--pcOpen = '(';
  2023. X    *pcClose = ')';
  2024. }
  2025. X
  2026. /*
  2027. X * scan the whatis structures for manual pages that don't exist        (ksb)
  2028. X * that are referenced in SEE ALSO lines
  2029. X */
  2030. int
  2031. AlsoScan(pWU, iCount)
  2032. WHATIS *pWU;
  2033. int iCount;
  2034. {
  2035. X    register int i, ch;
  2036. X    register char *pcAlso, *pcStart;
  2037. X
  2038. X    for (i = 0; i < iCount; ++i) {
  2039. X        if ((char *)0 == pWU[i].pcalso) {
  2040. X            /* no see also line in this page, skip it */
  2041. X            continue;
  2042. X        }
  2043. X        
  2044. X        pcStart = (char *)0;
  2045. X        /* printf("%s.%s:\n", pWU[i].pcbase, pWU[i].pcext); */
  2046. X        for (pcAlso = pWU[i].pcalso; '\000' != *pcAlso; ++pcAlso) {
  2047. X            if (',' != *pcAlso && !isspace(*pcAlso)) {
  2048. X                if ((char *)0 == pcStart) {
  2049. X                    pcStart = pcAlso;
  2050. X                }
  2051. X                continue;
  2052. X            }
  2053. X            if ((char *)0 == pcStart) {
  2054. X                continue;
  2055. X            }
  2056. X            ch = *pcAlso;
  2057. X            *pcAlso = '\000';
  2058. X            AlsoCheck(& pWU[i], pcStart);
  2059. X            *pcAlso = ch;
  2060. X            pcStart = (char *)0;
  2061. X        }
  2062. X        if ((char *)0 != pcStart) {
  2063. X            AlsoCheck(& pWU[i], pcStart);
  2064. X        }
  2065. X    }
  2066. X    return 1;
  2067. }
  2068. X
  2069. X
  2070. /*
  2071. X * look at all the manual pages in a directory                (ksb)
  2072. X *
  2073. X * Make sure they are compress'd or not...
  2074. X * When we find a symbolic link we verify it.
  2075. X * When we find a file we read it for makelinks, with that list in
  2076. X * hand we will rebuild the hard an symbols links later.
  2077. X * We delete lost links.
  2078. X */
  2079. int
  2080. AllDir(iSection, ppWU, piCount)
  2081. WHATIS **ppWU;
  2082. int iSection, *piCount;
  2083. {
  2084. X    extern int alphasort();
  2085. X    static char acDir[] = ".";
  2086. X    register struct direct *pDEPage, **ppDE;
  2087. X    register char *pcBase;
  2088. X    auto int i, l, k, npg;
  2089. X    auto FILE *fpFmt;
  2090. X    auto struct direct **ppDEPages;
  2091. X    auto char acLink[MAXPATHLEN+1];
  2092. X    auto char acName[MAXPATHLEN+1];
  2093. X    auto struct stat stCat;
  2094. X    auto int iw, iNeedFix;
  2095. X    auto WHATIS *pWU;
  2096. X    auto PATH PTTemp;
  2097. X
  2098. X    *ppWU = (WHATIS *)0;
  2099. X    *piCount = iw = 0;
  2100. X    if (-1 == (npg = scandir(acDir, & ppDEPages, catSelect, alphasort))) {
  2101. X        fprintf(stderr, "%s: scandir: %s: %s\n", progname, acDir, strerror(errno));
  2102. X        return 1;
  2103. X    }
  2104. X    if (0 == npg) {
  2105. #if 0
  2106. X        /* we don't output this because it makes all inits look broken
  2107. X         */
  2108. X        fprintf(fpOut, "%s: no pages in section %d\n", progname, iSection);
  2109. #endif
  2110. X        return 1;
  2111. X    }
  2112. X
  2113. X    if (0 != FixCompress(npg, ppDEPages, & iNeedFix) || fJustComp) {
  2114. X        free((char *)ppDEPages);
  2115. X        return 0;
  2116. X    }
  2117. X    free((char *)ppDEPages);
  2118. X
  2119. X    /* we cannot do the rest becuase FixCompress need to exec something
  2120. X     */
  2121. X    if (!fExec && 0 != iNeedFix) {
  2122. X        return 0;
  2123. X    }
  2124. X
  2125. X    if (-1 == (i = scandir(acDir, & ppDEPages, catSelect, alphasort))) {
  2126. X        fprintf(stderr, "%s: scandir: %s: %s\n", progname, acDir, strerror(errno));
  2127. X        return 1;
  2128. X    }
  2129. X
  2130. X    if (i > npg) {
  2131. X        fprintf(stderr, "%s: active manual system number of pages grew (%d > %d)\n", progname, i, npg);
  2132. X    }
  2133. X    npg = i;
  2134. X
  2135. X    /* this might be a gross over allocation, sigh
  2136. X     * we will patch it with realloc later...
  2137. X     */
  2138. X    if (0 == (pWU = (WHATIS *)malloc((unsigned)sizeof(WHATIS)*npg))) {
  2139. X        free((char *)ppDEPages);
  2140. X        fprintf(stderr, acNoMem, progname);
  2141. X        return 0;
  2142. X    }
  2143. X
  2144. X    if (fVerbose) {
  2145. X        ScanDups(npg, ppDEPages, pWU);
  2146. X    }
  2147. X
  2148. X    for (k = 0; k < npg; ++k) {
  2149. X        if ((struct direct *)0 == (pDEPage = ppDEPages[k])) {
  2150. X            continue;
  2151. X        }
  2152. X        if (-1 == LSTAT(pDEPage->d_name, & stCat)) {
  2153. X            continue;
  2154. X        }
  2155. X        /* ignore or bitch aboutr special files */
  2156. X        switch (stCat.st_mode & S_IFMT) {
  2157. #if defined(S_IFIFO)
  2158. X        case S_IFIFO:    /* fifo */
  2159. X            (void)fprintf(stderr, "%s: `%s\' is a fifo\n", progname, pDEPage->d_name);
  2160. X            ppDEPages[k] = 0;
  2161. X            free((char *)pDEPage);
  2162. X            continue;
  2163. #endif    /* no fifos        */
  2164. X
  2165. #if defined(S_IFSOCK)
  2166. X        case S_IFSOCK:    /* socket */
  2167. X            (void)fprintf(stderr, "%s: `%s\' is a socket\n", progname, pDEPage->d_name);
  2168. X            ppDEPages[k] = 0;
  2169. X            free((char *)pDEPage);
  2170. X            continue;
  2171. #endif    /* no sockets        */
  2172. X        case S_IFCHR:    /* character special */
  2173. X        case S_IFBLK:    /* block special */
  2174. X            fprintf(stderr, "%s: `%s\' is a special device\n", progname, pDEPage->d_name);
  2175. X            ppDEPages[k] = 0;
  2176. X            free((char *)pDEPage);
  2177. X            continue;
  2178. X        case S_IFDIR:    /* directory */
  2179. X            /* should get ., .., and OLD */
  2180. X            ppDEPages[k] = 0;
  2181. X            free((char *)pDEPage);
  2182. X            continue;
  2183. X
  2184. #if defined(S_IFLNK)
  2185. X        case S_IFLNK:    /* symbolic link */
  2186. X            /* check later... */
  2187. X            continue;
  2188. #endif    /* we can catch symbolic links for other reasons        */
  2189. X
  2190. X        case 0:
  2191. X        case S_IFREG:    /* regular */
  2192. X            pcBase = pDEPage->d_name;
  2193. X            ppDEPages[k] = 0;
  2194. X            break;
  2195. X        }
  2196. X
  2197. X        PTInit(&PTTemp, pcBase);
  2198. X
  2199. X        /* skip bad extenders        */
  2200. X        if (! PTHasExt(&PTTemp)) {
  2201. X            fprintf(stdout, "%s: `%s\' has no section extension\n", progname, pcBase);
  2202. X            continue;
  2203. X        }
  2204. X
  2205. X        /* open and read link info    */
  2206. X        if (PTIsComp(&PTTemp)) {
  2207. X            auto char acCmd[MAXPATHLEN+200];
  2208. X
  2209. X            sprintf(acCmd, "exec zcat \'%s\'", pcBase);
  2210. X            (void)fflush(stderr);
  2211. X            (void)fflush(stdout);
  2212. X            if (NULL == (fpFmt = popen(acCmd, "r"))) {
  2213. X                fprintf(stderr, "%s: popen: %s: %s\n", progname, acCmd, strerror(errno));
  2214. X                continue;
  2215. X            }
  2216. X            i = WUGrok(fpFmt, &PTTemp, & pWU[iw]);
  2217. X            (void)pclose(fpFmt);
  2218. X        } else {
  2219. X            if (NULL == (fpFmt = fopen(pcBase, "r"))) {
  2220. X                fprintf(stderr, "%s: fopen: %s: %s\n", progname, pcBase, strerror(errno));
  2221. X                continue;
  2222. X            }
  2223. X            i = WUGrok(fpFmt, &PTTemp, & pWU[iw]);
  2224. X            (void)fclose(fpFmt);
  2225. X        }
  2226. X        if (i != 0) {
  2227. X            /* cannot grok this page, skip it */
  2228. X            continue;
  2229. X        }
  2230. X
  2231. X        (void)sprintf(acName, "%s.%s%s", pWU[iw].pcbase, pWU[iw].pcext, fCompress ? acDotZ : "");
  2232. X
  2233. X        if (pWU[iw].isection != iSection) {
  2234. X            fprintf(fpOut, "%s: `%s\' might be in the wrong section\n", progname, pcBase);
  2235. X        }
  2236. X
  2237. X
  2238. X        /* here we've found (in pcBase) either
  2239. X         *    a/ the primary link to a page (good)
  2240. X         *    b/ a hard link to a page (fair)
  2241. X         *    c/ link to out of date page (yucko)
  2242. X         *    d/ a page whoose primary link is gone? (ouch!)
  2243. X         */
  2244. X        if (0 != strcmp(pcBase, acName)) {    /* (b)? */
  2245. X            ppDE = FindDEnt(acName, ppDEPages, npg);
  2246. X            if ((struct direct **)0 != ppDE) {
  2247. #if HAVE_SLINKS
  2248. X                if (0 == LSTAT(acName, &stCat)) {
  2249. X                    switch ((stCat.st_mode & S_IFMT)) {
  2250. X                    case S_IFLNK:
  2251. X                        if (fExec && fMkLinks) {
  2252. X                            sym2Hard(acName);
  2253. X                        }
  2254. X                        break;
  2255. X                    case S_IFREG:
  2256. X                        break;
  2257. X                    default:
  2258. X                        fprintf(stderr, "%s: type type error\n", progname);
  2259. X                        abort();
  2260. X                    }
  2261. X                }
  2262. #endif
  2263. X                if (!CmpFile(pcBase, acName)) {
  2264. X                    fprintf(fpOut, "%s: would have scanned `%s\' as `%s\', but they are not identical\n", progname, pcBase, acName);
  2265. X                } else {
  2266. X                    fprintf(fpOut, "%s: scan `%s\' as `%s\'\n", progname, pcBase, acName);
  2267. X                    ppDEPages[k] = pDEPage;
  2268. X                    pDEPage = *ppDE;
  2269. X                    *ppDE = 0;
  2270. X                    pcBase = pDEPage->d_name;
  2271. X                }
  2272. X            } else { /* (d) */
  2273. X                fprintf(stderr, "%s: primary link to %s should be %s\n", progname, pcBase, acName);
  2274. X                if (fVerbose) {
  2275. X                    (void)printf("%s: ln \"%s\" \"%s\"\n", progname, pcBase, acName);
  2276. X                }
  2277. X                if (fExec && fMkLinks && -1 == link(pcBase, acName)) {
  2278. X                    fprintf(stderr, "%s: link: %s: %s\n", progname, acName, strerror(errno));
  2279. X                }
  2280. X            }
  2281. X        }
  2282. X        MkLinks(acName, pWU+iw, ppDEPages, npg, &stCat, 0);
  2283. X        ++iw;
  2284. X    }
  2285. X
  2286. #if HAVE_SLINKS
  2287. X    /* the files left are links that point nowhere, or to files
  2288. X     * that do not what them (lost a subcommand?)
  2289. X     */
  2290. X    for (l = 0; l < npg; ++l) {
  2291. X        if ((struct direct *)0 == (pDEPage = ppDEPages[l])) {
  2292. X            continue;
  2293. X        }
  2294. X        if (-1 == (i = readlink(pDEPage->d_name, acLink, MAXPATHLEN))) {
  2295. X            if (errno == EINVAL) {
  2296. X                (void)printf("%s: %s: manual page under the wrong name?\n", progname, pDEPage->d_name);
  2297. X                continue;
  2298. X            }
  2299. X            fprintf(stderr, "%s: readlink: %s: %s\n", progname, pDEPage->d_name, strerror(errno));
  2300. X            continue;
  2301. X        }
  2302. X        acLink[i] = '\000';
  2303. X        fprintf(fpOut, "%s: `%s\' is an unclaimed symbolic link", progname, pDEPage->d_name);
  2304. X        if (0 == access(acLink, R_OK)) {
  2305. X            fprintf(fpOut, " to `%s\'\n", acLink);
  2306. X        } else {
  2307. X            fprintf(fpOut, ", leading nowhere\n");
  2308. X        }
  2309. X    }
  2310. #endif
  2311. X
  2312. X    /* now we can realloc the WHATIS array to the correct size
  2313. X     */
  2314. X    if (iw < npg) {
  2315. X        if (0 == (pWU = (WHATIS *)realloc((char *)pWU, (unsigned)sizeof(WHATIS)*iw))) {
  2316. X            fprintf(stderr, "%s: realloc compression failed\n", progname);
  2317. X            return 0;
  2318. X        }
  2319. X        *ppWU = pWU;
  2320. X    }
  2321. X    *piCount = iw;
  2322. X
  2323. X    return 1;
  2324. }
  2325. X
  2326. /*
  2327. X * this routine is kinda kludge.  MkLinks wants to be in the dir it    (ksb)
  2328. X * is making the links for, so be cd there, do it, and come back.
  2329. X *
  2330. X * We could fork and wait if that would be more `safe'
  2331. X * (this breaks if we are in a directory we cannot read, for example)
  2332. X */
  2333. void
  2334. ModLinks(pPTDest, pWU, ppDEPages, npg, pstCat, fRemove)
  2335. PATH *pPTDest;
  2336. WHATIS *pWU;
  2337. struct direct **ppDEPages;
  2338. int npg, fRemove;
  2339. struct stat *pstCat;
  2340. {
  2341. X    extern char *getwd();
  2342. X    auto char acPwd[MAXPATHLEN+1];
  2343. X    auto int fSave;
  2344. X
  2345. X    (void)fflush(stdout);
  2346. X    if ((char *)0 == getwd(acPwd)) {
  2347. X        fprintf(stderr, "%s: %s\n", progname, acPwd);
  2348. X        return;
  2349. X    }
  2350. X    if (-1 == chdir(PTDir(pPTDest))) {
  2351. X        fprintf(stderr, "%s: chdir: %s: %s\n", progname, PTDir(pPTDest), strerror(errno));
  2352. X        return;
  2353. X    }
  2354. X
  2355. X    fSave = fMkLinks;
  2356. X    fMkLinks = 1;
  2357. X    MkLinks(PTLocal(pPTDest), pWU, ppDEPages, npg, pstCat, fRemove);
  2358. X    fMkLinks = fSave;
  2359. X
  2360. X    if (-1 == chdir(acPwd)) {
  2361. X        fprintf(stderr, "%s: chdir: %s: %s\n", progname, acPwd, strerror(errno));
  2362. X        exit(1);
  2363. X    }
  2364. }
  2365. Purdue
  2366. chmod 0444 mkcat/scan.c ||
  2367. echo 'restore of mkcat/scan.c failed'
  2368. Wc_c="`wc -c < 'mkcat/scan.c'`"
  2369. test 27828 -eq "$Wc_c" ||
  2370.     echo 'mkcat/scan.c: original size 27828, current size' "$Wc_c"
  2371. fi
  2372. true || echo 'restore of mkcat/genwhatis.c failed'
  2373. echo End of part 2, continue with part 3
  2374. exit 0
  2375.  
  2376. exit 0 # Just in case...
  2377.