home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume13 / file / part01 next >
Encoding:
Internet Message Format  |  1988-01-30  |  36.4 KB

  1. Subject:  v13i027:  Replacement for the file(1) program, Part01/02
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: "Ian F. Darwin" <ian@sq.com>
  7. Posting-number: Volume 13, Issue 27
  8. Archive-name: file/part01
  9.  
  10. This is Ian Darwin's (copyright but distributable) file(1) command. It
  11. follows the USG (Sys V) model of the file command, rather than the
  12. Research (V7) version or the V7-derived Berkeley one. That is, there is a
  13. file (/etc/magic) that contains much of the ritual information that is the
  14. source of this program's power. It knows a little more magic (including
  15. tar archives) than System V; the /etc/magic parsing seems to be compatible
  16. with the (poorly documented) System V /etc/magic format (with one
  17. exception; see the man page).
  18.  
  19. : To unbundle, sh this file
  20. mkdir magdir tst
  21. echo x - LEGAL.NOTICE 1>&2
  22. cat >LEGAL.NOTICE <<'@@@End of LEGAL.NOTICE'
  23. Copyright (c) Ian F. Darwin 1986, 1987.
  24. Written by Ian F. Darwin.
  25.  
  26. This software is not subject to any license of the American Telephone
  27. and Telegraph Company or of the Regents of the University of California.
  28.  
  29. Permission is granted to anyone to use this software for any purpose on
  30. any computer system, and to alter it and redistribute it freely, subject
  31. to the following restrictions:
  32.  
  33. 1. The author is not responsible for the consequences of use of this
  34.    software, no matter how awful, even if they arise from flaws in it.
  35.  
  36. 2. The origin of this software must not be misrepresented, either by
  37.    explicit claim or by omission.  Since few users ever read sources,
  38.    credits must appear in the documentation.
  39.  
  40. 3. Altered versions must be plainly marked as such, and must not be
  41.    misrepresented as being the original software.  Since few users
  42.    ever read sources, credits must appear in the documentation.
  43.  
  44. 4. This notice may not be removed or altered.
  45. @@@End of LEGAL.NOTICE
  46. echo x - README 1>&2
  47. cat >README <<'@@@End of README'
  48. ** README for file(1) Command **
  49. @(#) $Header: README,v 1.9 87/11/07 12:38:30 ian Exp $
  50.  
  51. This is Ian Darwin's (copyright but distributable) file(1)
  52. command. It follows the USG (Sys V) model of the file command,
  53. rather than the Research (V7) version or the V7-derived Berkeley
  54. one. That is, there is a file (/etc/magic) that contains much
  55. of the ritual information that is the source of this program's
  56. power. It knows a little more magic (including tar archives)
  57. than System V; the /etc/magic parsing seems to be compatible
  58. with the (poorly documented) System V /etc/magic format (with
  59. one exception; see the man page).
  60.  
  61. In addition, the /etc/magic file is built from a subdirectory
  62. for easier maintenance.  I will act as a clearinghouse for
  63. magic numbers assigned to all sorts of data files that
  64. are in reasonable circulation. Send your magic numbers,
  65. in magic(4) format please, to the author, Ian Darwin,
  66. {utzoo|ihnp4}!darwin!ian, ian@sq.com. 
  67.  
  68. LEGAL.NOTICE - read this first.
  69. README - read this second (you are currently reading this file).
  70. PORTING - read this only if the program won't compile.
  71. Makefile - read this next, adapt it as needed (particularly
  72.     the location of the old existing file command and
  73.     the man page layouts), type "make" to compile, 
  74.     "make try" to try it out against your old version.
  75.     Expect some diffs, particularly since your original
  76.     file(1) may not grok the imbedded-space ("\ ") in
  77.     the current magic file, or may even not use the
  78.     magic file.
  79. apprentice.c - parses /etc/magic to learn magic
  80. ascmagic.c - third & last set of tests, based on hardwired assumptions.
  81. core - not included in distribution due to mailer limitations.
  82. debug.c - includes -c printout routine
  83. file.1 - man page for the command
  84. magic.4 - man page for the magic file, courtesy Guy Harris.
  85.     Install as magic.4 on USG and magic.5 on V7 or Berkeley; cf Makefile.
  86. file.c - main program
  87. file.h - header file
  88. fsmagic.c - first set of tests the program runs, based on filesystem info
  89. is_tar.c - knows about tarchives (courtesy John Gilmore).
  90. magdir - directory of /etc/magic pieces
  91. names.h - header file for ascmagic.c
  92. softmagic.c - 2nd set of tests, based on /etc/magic
  93. strtok.c, getopt.c - in case you them (courtesy of Henry Spencer).
  94. strtol.c, strchr.c - in case you need them - public domain.
  95. tst - simple test suite, built from tst/Makefile
  96. @@@End of README
  97. echo x - PORTING 1>&2
  98. cat >PORTING <<'@@@End of PORTING'
  99. Portability of the new file(1) command.
  100. @(#) $Header: PORTING,v 1.6 87/11/08 23:03:41 ian Exp $
  101.  
  102. Read this file only if the program doesn't compile on your system.
  103.  
  104. I have tried to make a program that doesn't need any command-line
  105. defines (-D) to specify what version of UNIX is in use,
  106. by using the definitions available in the system #include
  107. files. For example, the lstat(2) call is normally found in
  108. 4BSD systems, but might be grafted into some other variant
  109. of UNIX. If it's done right (ie., using the same definitions),
  110. my program will compile and work correctly. Look at the #ifdefs
  111. to see how it's done. 
  112.  
  113. I've also tried to include all the non-portable library routines
  114. I used (getopt, str*).   Non-portable here means `not in every
  115. reasonably standard UNIX out there: V7, System V, 4BSD'.
  116.  
  117. There is one area that just might cause problems. On System
  118. V, they moved the definition of major() and minor() out of
  119. <sys/types.h> into <sys/sysmacros.h>.  Hence, if major isn't
  120. defined after including types.h, I automatically include sys/sysmacros.h.
  121. This will work for 99% of the systems out there. ONLY if you
  122. have a system in which  neither types.h nor sysmacros.h defines
  123. `major' will this automatic include fail (I hope). On such
  124. systems, you will get a compilation error in trying to compile
  125. a warning message. Please do the following: 
  126.  
  127.     1) change the appropriate (2nd) #include at the start of 
  128.         fsmagic.c
  129. and    2) let me know the name of the system, the release number,
  130.        and the name of the header file that *does* include
  131.        this "standard" definition.
  132.  
  133. If you are running the old Ritchie PDP-11 C compiler or
  134. some other compiler that doesn't know about `void', you will have
  135. to un-comment-out the definition of `void=int' in the Makefile.
  136.  
  137. Other than this, there should be no portability problems,
  138. but one never knows these days. Please let me know of any
  139. other problems you find porting to a UNIX system. I don't much
  140. care for non-UNIX systems but will collect widely-used magic 
  141. numbers for them as well as for UNIX systems.
  142.  
  143. Ian Darwin
  144. Toronto, Canada
  145. @@@End of PORTING
  146. echo x - file.c 1>&2
  147. cat >file.c <<'@@@End of file.c'
  148. /*
  149.  * file - find type of a file or files - main program.
  150.  *
  151.  * Copyright (c) Ian F. Darwin, 1987.
  152.  * Written by Ian F. Darwin.
  153.  *
  154.  * This software is not subject to any license of the American Telephone
  155.  * and Telegraph Company or of the Regents of the University of California.
  156.  *
  157.  * Permission is granted to anyone to use this software for any purpose on
  158.  * any computer system, and to alter it and redistribute it freely, subject
  159.  * to the following restrictions:
  160.  *
  161.  * 1. The author is not responsible for the consequences of use of this
  162.  *    software, no matter how awful, even if they arise from flaws in it.
  163.  *
  164.  * 2. The origin of this software must not be misrepresented, either by
  165.  *    explicit claim or by omission.  Since few users ever read sources,
  166.  *    credits must appear in the documentation.
  167.  *
  168.  * 3. Altered versions must be plainly marked as such, and must not be
  169.  *    misrepresented as being the original software.  Since few users
  170.  *    ever read sources, credits must appear in the documentation.
  171.  *
  172.  * 4. This notice may not be removed or altered.
  173.  */
  174.  
  175. #include <stdio.h>
  176. #include <sys/types.h>
  177. #include <sys/stat.h>
  178. #include "file.h"
  179.  
  180. #define USAGE        "usage: %s [-c] [-f namefile] [-m magicfile] file...\n"
  181.  
  182. #ifndef    lint
  183. static char *moduleid = 
  184.     "@(#)$Header: file.c,v 1.14 87/11/12 13:11:06 ian Exp $";
  185. #endif    /* lint */
  186. extern char *ckfmsg;
  187. int     debug = 0,     /* huh? */
  188.     nbytes = 0,    /* number of bytes read from a datafile */
  189.     nmagic = 0;    /* number of valid magic[]s */
  190. FILE *efopen();
  191. #ifdef MAGIC
  192. char *magicfile = MAGIC;    /* where magic be found */
  193. #else
  194. char *magicfile = "/etc/magic";    /* where magic be found */
  195. #endif
  196. char *progname;
  197. struct stat statbuf;
  198. struct utimbuf {    /* for utime(2), belongs in a .h file */
  199.     time_t actime;    /* access time */
  200.     time_t modtime;    /* modification time */
  201. };
  202.  
  203. /*
  204.  * main - parse arguments and handle options
  205.  */
  206. main(argc, argv)
  207. int argc;
  208. char *argv[];
  209. {
  210.     int c;
  211.     int check = 0, didsomefiles = 0, errflg = 0, ret = 0;
  212.     extern int optind;
  213.     extern char *optarg;
  214.  
  215.     progname = argv[0];
  216.  
  217.     while ((c = getopt(argc, argv, "cdf:m:")) != EOF)
  218.         switch (c) {
  219.         case 'c':
  220.             ++check;
  221.             break;
  222.         case 'd':
  223.             ++debug;
  224.             break;
  225.         case 'f':
  226.             unwrap(optarg);
  227.             ++didsomefiles;
  228.             break;
  229.         case 'm':
  230.             magicfile = optarg;
  231.             break;
  232.         case '?':
  233.         default:
  234.             errflg++;
  235.             break;
  236.         }
  237.     if (errflg) {
  238.         (void) fprintf(stderr, USAGE, progname);
  239.         exit(2);
  240.     }
  241.  
  242.     ret = apprentice(magicfile, check);
  243.     if (check)
  244.         exit(ret);
  245.  
  246.     if (optind == argc) {
  247.         if (!didsomefiles)
  248.             (void)fprintf(stderr, USAGE, progname);
  249.     }
  250.     else
  251.         for (; optind < argc; optind++)
  252.             process(argv[optind]);
  253.  
  254.     exit(0);
  255. }
  256.  
  257. /*
  258.  * unwrap -- read a file of filenames, do each one.
  259.  */
  260. unwrap(fn)
  261. char *fn;
  262. {
  263. #define FILENAMELEN 128
  264.     char buf[FILENAMELEN];
  265.     FILE *f;
  266.  
  267.     if ((f = fopen(fn, "r")) == NULL)
  268.         (void) fprintf(stderr, "%s: file %s unreadable\n",
  269.             progname, fn);
  270.     else {
  271.         while (fgets(buf, FILENAMELEN, f) != NULL) {
  272.             buf[strlen(buf)-1] = '\0';
  273.             process(buf);
  274.         }
  275.         (void) fclose(f);
  276.     }
  277. }
  278.  
  279. /*
  280.  * process - process input file
  281.  */
  282. process(inname)
  283. char    *inname;
  284. {
  285.     int    fd;
  286.     char    buf[HOWMANY];
  287.     struct utimbuf utbuf;
  288.  
  289.     if (strcmp("-", inname) == 0) {
  290.         (void) printf("standard input:\t");
  291.         if (fstat(0, &statbuf)<0)
  292.             warning("cannot fstat; ");
  293.         fd = 0;
  294.         goto readit;
  295.     }
  296.         
  297.     (void) printf("%s:\t", inname);
  298.  
  299.     /*
  300.      * first try judging the file based on its filesystem status
  301.      * Side effect: fsmagic updates global data `statbuf'.
  302.      */
  303.     if (fsmagic(inname) != 0) {
  304.         /*NULLBODY*/;
  305.     } else if ((fd = open(inname, 0)) < 0) {
  306.         /* We can't open it, but we were able to stat it. */
  307.         if (statbuf.st_mode & 0002) ckfputs("writeable, ", stdout);
  308.         if (statbuf.st_mode & 0111) ckfputs("executable, ", stdout);
  309.         warning("can't read");
  310.     } else {
  311. readit:
  312.         /*
  313.          * try looking at the first HOWMANY bytes
  314.          */
  315.         if ((nbytes = read(fd, buf, HOWMANY)) == -1)
  316.             warning("read failed");
  317.         if (nbytes == 0) {
  318.             ckfputs("empty", stdout);
  319.         } else
  320.         /*
  321.          * try tests in /etc/magic (or surrogate magic file)
  322.          */
  323.         if (softmagic(buf) == 1)
  324.             /*NULLBODY*/;
  325.         else if (ascmagic(buf) == 1)
  326.             /*
  327.              * try known keywords, check for ascii-ness too.
  328.              */
  329.             /*NULLBODY*/;
  330.         else {
  331.             /*
  332.              * abandon hope, all ye who remain here
  333.              */
  334.             ckfputs("data", stdout);
  335.         }
  336.         if (strcmp("-", inname) != 0) {
  337.             /*
  338.              * Restore access, modification times if we read it.
  339.              */
  340.             utbuf.actime = statbuf.st_atime;
  341.             utbuf.modtime = statbuf.st_mtime;
  342.             (void) utime(inname, &utbuf);
  343.             /* we don't care if we lack perms */
  344.             (void) close(fd);
  345.         }
  346.     }
  347.  
  348.     (void) putchar('\n');
  349. }
  350.  
  351.  
  352. @@@End of file.c
  353. echo x - apprentice.c 1>&2
  354. cat >apprentice.c <<'@@@End of apprentice.c'
  355. /*
  356.  * apprentice - make one pass through /etc/magic, learning its secrets.
  357.  *
  358.  * Copyright (c) Ian F. Darwin, 1987.
  359.  * Written by Ian F. Darwin.
  360.  *
  361.  * This software is not subject to any license of the American Telephone
  362.  * and Telegraph Company or of the Regents of the University of California.
  363.  *
  364.  * Permission is granted to anyone to use this software for any purpose on
  365.  * any computer system, and to alter it and redistribute it freely, subject
  366.  * to the following restrictions:
  367.  *
  368.  * 1. The author is not responsible for the consequences of use of this
  369.  *    software, no matter how awful, even if they arise from flaws in it.
  370.  *
  371.  * 2. The origin of this software must not be misrepresented, either by
  372.  *    explicit claim or by omission.  Since few users ever read sources,
  373.  *    credits must appear in the documentation.
  374.  *
  375.  * 3. Altered versions must be plainly marked as such, and must not be
  376.  *    misrepresented as being the original software.  Since few users
  377.  *    ever read sources, credits must appear in the documentation.
  378.  *
  379.  * 4. This notice may not be removed or altered.
  380.  */
  381.  
  382. #include <stdio.h>
  383. #include <ctype.h>
  384. #include "file.h"
  385.  
  386. #ifndef    lint
  387. static char *moduleid = 
  388.     "@(#)$Header: apprentice.c,v 1.8 87/11/06 21:14:34 ian Exp $";
  389. #endif    /* lint */
  390.  
  391. #define MAXSTR        500
  392. #define    EATAB {while (isascii(*l) && isspace(*l))  ++l;}
  393.  
  394. extern char *progname;
  395. extern char *magicfile;
  396. extern int debug;        /* option */
  397. extern int nmagic;        /* number of valid magic[]s */
  398. extern long strtol();
  399.  
  400. struct magic magic[MAXMAGIS];
  401.  
  402. char *getstr();
  403.  
  404. apprentice(fn, check)
  405. char *fn;            /* name of magic file */
  406. int check;        /* non-zero: checking-only run. */
  407. {
  408.     FILE *f;
  409.     char line[MAXSTR+1];
  410.     int errs = 0;
  411.  
  412.     f = fopen(fn, "r");
  413.     if (f==NULL) {
  414.         (void) fprintf(stderr, "%s: can't read magic file %s\n",
  415.         progname, fn);
  416.         if (check)
  417.             return -1;
  418.         else
  419.             exit(1);
  420.     }
  421.  
  422.     /* parse it */
  423.     if (check)    /* print silly verbose header for USG compat. */
  424.         (void) printf("cont\toffset\ttype\topcode\tvalue\tdesc\n");
  425.  
  426.     while (fgets(line, MAXSTR, f) != NULL) {
  427.         if (line[0]=='#')    /* comment, do not parse */
  428.             continue;
  429.         if (strlen(line) <= 1)    /* null line, garbage, etc */
  430.             continue;
  431.         line[strlen(line)-1] = '\0'; /* delete newline */
  432.         if (parse(line, &nmagic, check) != 0)
  433.             ++errs;
  434.     }
  435.  
  436.     (void) fclose(f);
  437.     return errs ? -1 : 0;
  438. }
  439.  
  440. /*
  441.  * parse one line from magic file, put into magic[index++] if valid
  442.  */
  443. int
  444. parse(l, ndx, check)
  445. char *l;
  446. int *ndx, check;
  447. {
  448.     int i = 0, nd = *ndx;
  449.     int slen;
  450.     static int warned = 0;
  451.     struct magic *m;
  452.     extern int errno;
  453.  
  454.     /*
  455.      * TODO malloc the magic structures (linked list?) so this can't happen
  456.      */
  457.     if (nd+1 >= MAXMAGIS){
  458.         if (warned++ == 0)
  459.             warning(
  460. "magic table overflow - increase MAXMAGIS beyond %d in file/apprentice.c\n",
  461.             MAXMAGIS);
  462.         return -1;
  463.     }
  464.     m = &magic[*ndx];
  465.  
  466.     if (*l == '>') {
  467.         ++l;        /* step over */
  468.         m->contflag = 1;
  469.     } else
  470.         m->contflag = 0;
  471.  
  472.     /* get offset, then skip over it */
  473.     m->offset = atoi(l);
  474.     while (isascii(*l) && isdigit(*l))
  475.         ++l;
  476.     EATAB;
  477.  
  478. #define NBYTE 4
  479. #define NSHORT 5
  480. #define NLONG 4
  481. #define NSTRING 6
  482.     /* get type, skip it */
  483.     if (strncmp(l, "byte", NBYTE)==0) {
  484.         m->type = BYTE;
  485.         l += NBYTE;
  486.     } else if (strncmp(l, "short", NSHORT)==0) {
  487.         m->type = SHORT;
  488.         l += NSHORT;
  489.     } else if (strncmp(l, "long", NLONG)==0) {
  490.         m->type = LONG;
  491.         l += NLONG;
  492.     } else if (strncmp(l, "string", NSTRING)==0) {
  493.         m->type = STRING;
  494.         l += NSTRING;
  495.     } else {
  496.         errno = 0;
  497.         warning("type %s invalid", l);
  498.         return -1;
  499.     }
  500.     EATAB;
  501.  
  502.     if (*l == '>' || *l == '<' || *l == '&' || *l == '=') {
  503.         m->reln = *l;
  504.         ++l;
  505.     } else
  506.         m->reln = '=';
  507.     EATAB;
  508.  
  509. /*
  510.  * TODO finish this macro and start using it!
  511.  * #define offsetcheck {if (offset > HOWMANY-1) warning("offset too big"); }
  512.  */
  513.     switch(m->type) {
  514.     /*
  515.      * Do not remove the casts below.  They are vital.
  516.      * When later compared with the data, the sign extension must
  517.      * have happened.
  518.      */
  519.     case BYTE:
  520.         m->value.l = (char) strtol(l,&l,0);
  521.         break;
  522.     case SHORT:
  523.         m->value.l = (short) strtol(l,&l,0);
  524.         break;
  525.     case LONG:
  526.         m->value.l = (long) strtol(l,&l,0);
  527.         break;
  528.     case STRING:
  529.         l = getstr(l, m->value.s, sizeof(m->value.s), &slen);
  530.         m->vallen = slen;
  531.         break;
  532.     default:
  533.         warning("can't happen: m->type=%d\n", m->type);
  534.         return -1;
  535.     }
  536.  
  537.     /*
  538.      * now get last part - the description
  539.      */
  540.     EATAB;
  541.     while ((m->desc[i++] = *l++) != '\0' && i<MAXDESC)
  542.         /* NULLBODY */;
  543.  
  544.     if (check) {
  545.         mdump(m);
  546.     }
  547.     ++(*ndx);        /* make room for next */
  548.     return 0;
  549. }
  550.  
  551. /*
  552.  * Convert a string containing C character escapes.  Stop at an unescaped
  553.  * space or tab.
  554.  * Copy the converted version to "p", returning its length in *slen.
  555.  * Return updated scan pointer as function result.
  556.  */
  557. char *
  558. getstr(s, p, plen, slen)
  559. register char    *s;
  560. register char    *p;
  561. int    plen, *slen;
  562. {
  563.     char    *origs = s, *origp = p;
  564.     char    *pmax = p + plen - 1;
  565.     register int    c;
  566.     register int    val;
  567.  
  568.     while((c = *s++) != '\0') {
  569.         if (isspace(c)) break;
  570.         if (p >= pmax) {
  571.             fprintf(stderr, "String too long: %s\n", origs);
  572.             break;
  573.         }
  574.         if(c == '\\') {
  575.             switch(c = *s++) {
  576.  
  577.             case '\0':
  578.                 goto out;
  579.  
  580.             default:
  581.                 *p++ = c;
  582.                 break;
  583.  
  584.             case 'n':
  585.                 *p++ = '\n';
  586.                 break;
  587.  
  588.             case 'r':
  589.                 *p++ = '\r';
  590.                 break;
  591.  
  592.             case 'b':
  593.                 *p++ = '\b';
  594.                 break;
  595.  
  596.             case 't':
  597.                 *p++ = '\t';
  598.                 break;
  599.  
  600.             case 'f':
  601.                 *p++ = '\f';
  602.                 break;
  603.  
  604.             case 'v':
  605.                 *p++ = '\v';
  606.                 break;
  607.  
  608.             /* \ and up to 3 octal digits */
  609.             case '0':
  610.             case '1':
  611.             case '2':
  612.             case '3':
  613.             case '4':
  614.             case '5':
  615.             case '6':
  616.             case '7':
  617.                 val = c - '0';
  618.                 c = *s++;  /* try for 2 */
  619.                 if(c >= '0' && c <= '7') {
  620.                     val = (val<<3) | (c - '0');
  621.                     c = *s++;  /* try for 3 */
  622.                     if(c >= '0' && c <= '7')
  623.                         val = (val<<3) | (c-'0');
  624.                     else
  625.                         --s;
  626.                 }
  627.                 else
  628.                     --s;
  629.                 *p++ = val;
  630.                 break;
  631.  
  632.             /* \x and up to 3 hex digits */
  633.             case 'x':
  634.                 val = 'x';    /* Default if no digits */
  635.                 c = hextoint(*s++);    /* Get next char */
  636.                 if (c >= 0) {
  637.                     val = c;
  638.                     c = hextoint(*s++);
  639.                     if (c >= 0) {
  640.                         val = (val << 4) + c;
  641.                         c = hextoint(*s++);
  642.                         if (c >= 0) {
  643.                             val = (val << 4) + c;
  644.                         } else
  645.                             --s;
  646.                     } else
  647.                         --s;
  648.                 } else
  649.                     --s;
  650.                 *p++ = val;
  651.                 break;
  652.             }
  653.         } else
  654.             *p++ = c;
  655.     }
  656. out:
  657.     *p = '\0';
  658.     *slen = p - origp;
  659.     return(s);
  660. }
  661.  
  662.  
  663. /* Single hex char to int; -1 if not a hex char. */
  664. int
  665. hextoint(c)
  666.     char c;
  667. {
  668.     if (!isascii(c))    return -1;
  669.     if (isdigit(c))        return c - '0';
  670.     if ((c>='a')&(c<='f'))    return c + 10 - 'a';
  671.     if ((c>='A')&(c<='F'))    return c + 10 - 'A';
  672.                 return -1;
  673. }
  674.  
  675.  
  676. /*
  677.  * Print a string containing C character escapes.
  678.  */
  679. void
  680. showstr(s)
  681. register char    *s;
  682. {
  683.     register char    c;
  684.  
  685.     while((c = *s++) != '\0') {
  686.         if(c >= 040 && c <= 0176)
  687.             putchar(c);
  688.         else {
  689.             putchar('\\');
  690.             switch (c) {
  691.             
  692.             case '\n':
  693.                 putchar('n');
  694.                 break;
  695.  
  696.             case '\r':
  697.                 putchar('r');
  698.                 break;
  699.  
  700.             case '\b':
  701.                 putchar('b');
  702.                 break;
  703.  
  704.             case '\t':
  705.                 putchar('t');
  706.                 break;
  707.  
  708.             case '\f':
  709.                 putchar('f');
  710.                 break;
  711.  
  712.             case '\v':
  713.                 putchar('v');
  714.                 break;
  715.  
  716.             default:
  717.                 printf("%.3o", c & 0377);
  718.                 break;
  719.             }
  720.         }
  721.     }
  722.     putchar('\t');
  723. }
  724. @@@End of apprentice.c
  725. echo x - fsmagic.c 1>&2
  726. cat >fsmagic.c <<'@@@End of fsmagic.c'
  727. /*
  728.  * fsmagic - magic based on filesystem info - directory, special files, etc.
  729.  *
  730.  * Copyright (c) Ian F. Darwin, 1987.
  731.  * Written by Ian F. Darwin.
  732.  *
  733.  * This software is not subject to any license of the American Telephone
  734.  * and Telegraph Company or of the Regents of the University of California.
  735.  *
  736.  * Permission is granted to anyone to use this software for any purpose on
  737.  * any computer system, and to alter it and redistribute it freely, subject
  738.  * to the following restrictions:
  739.  *
  740.  * 1. The author is not responsible for the consequences of use of this
  741.  *    software, no matter how awful, even if they arise from flaws in it.
  742.  *
  743.  * 2. The origin of this software must not be misrepresented, either by
  744.  *    explicit claim or by omission.  Since few users ever read sources,
  745.  *    credits must appear in the documentation.
  746.  *
  747.  * 3. Altered versions must be plainly marked as such, and must not be
  748.  *    misrepresented as being the original software.  Since few users
  749.  *    ever read sources, credits must appear in the documentation.
  750.  *
  751.  * 4. This notice may not be removed or altered.
  752.  */
  753.  
  754. #include <stdio.h>
  755. #include <sys/types.h>
  756. #ifndef    major            /* if `major' not defined in types.h, */
  757. #include <sys/sysmacros.h>    /* try this one. */
  758. #endif
  759. #ifndef    major    /* still not defined? give up, manual intervention needed */
  760.         /* If cc tries to compile this, read and act on it. */
  761.         /* On most systems cpp will discard it automatically */
  762.         Congratulations, you have found a portability bug.
  763.         Please grep /usr/include/sys and edit the above #include 
  764.         to point at the file that defines the `major' macro.
  765. #endif    /*major*/
  766. #include <sys/stat.h>
  767. #include "file.h"
  768.  
  769. #ifndef    lint
  770. static char *moduleid = 
  771.     "@(#)$Header: fsmagic.c,v 1.8 88/01/15 12:13:52 ian Exp $";
  772. #endif    /* lint */
  773.  
  774. extern char *progname;
  775. extern char *ckfmsg, *magicfile;
  776. extern int debug;
  777. extern FILE *efopen();
  778.  
  779. fsmagic(fn)
  780. char *fn;
  781. {
  782.     extern struct stat statbuf;
  783.  
  784.     /*
  785.      * Fstat is cheaper but fails for files you don't have read perms on.
  786.      * On 4.2BSD and similar systems, use lstat() so identify symlinks.
  787.      */
  788. #ifdef    S_IFLNK
  789.     if (lstat(fn, &statbuf) <0)
  790. #else
  791.     if (stat(fn, &statbuf) <0)
  792. #endif
  793.         {
  794.             warning("can't stat", "");
  795.             return -1;
  796.         }
  797.  
  798.     if (statbuf.st_mode & S_ISUID) ckfputs("setuid ", stdout);
  799.     if (statbuf.st_mode & S_ISGID) ckfputs("setgid ", stdout);
  800.     if (statbuf.st_mode & S_ISVTX) ckfputs("sticky ", stdout);
  801.     
  802.     switch (statbuf.st_mode & S_IFMT) {
  803.     case S_IFDIR:
  804.         ckfputs("directory", stdout);
  805.         return 1;
  806.     case S_IFCHR:
  807.         (void) printf("character special (%d/%d)",
  808.             major(statbuf.st_rdev), minor(statbuf.st_rdev));
  809.         return 1;
  810.     case S_IFBLK:
  811.         (void) printf("block special (%d/%d)",
  812.             major(statbuf.st_rdev), minor(statbuf.st_rdev));
  813.         return 1;
  814.     /* TODO add code to handle V7 MUX and Blit MUX files */
  815. #ifdef    S_IFIFO
  816.     case S_IFIFO:
  817.         ckfputs("fifo (named pipe)", stdout);
  818.         return 1;
  819. #endif
  820. #ifdef    S_IFLNK
  821.     case S_IFLNK:
  822.         ckfputs("symbolic link", stdout);
  823.         return 1;
  824. #endif
  825. #ifdef    S_IFSOCK
  826.     case S_IFSOCK:
  827.         ckfputs("socket", stdout);
  828.         return 1;
  829. #endif
  830.     case S_IFREG:
  831.         break;
  832.     default:
  833.         warning("invalid st_mode %d in statbuf!", statbuf.st_mode);
  834.     }
  835.  
  836.     /*
  837.      * regular file, check next possibility
  838.      */
  839.     if (statbuf.st_size == 0) {
  840.         ckfputs("empty", stdout);
  841.         return 1;
  842.     }
  843.     return 0;
  844. }
  845.  
  846. @@@End of fsmagic.c
  847. echo x - softmagic.c 1>&2
  848. cat >softmagic.c <<'@@@End of softmagic.c'
  849. /*
  850.  * softmagic - interpret variable magic from /etc/magic
  851.  *
  852.  * Copyright (c) Ian F. Darwin, 1987.
  853.  * Written by Ian F. Darwin.
  854.  *
  855.  * This software is not subject to any license of the American Telephone
  856.  * and Telegraph Company or of the Regents of the University of California.
  857.  *
  858.  * Permission is granted to anyone to use this software for any purpose on
  859.  * any computer system, and to alter it and redistribute it freely, subject
  860.  * to the following restrictions:
  861.  *
  862.  * 1. The author is not responsible for the consequences of use of this
  863.  *    software, no matter how awful, even if they arise from flaws in it.
  864.  *
  865.  * 2. The origin of this software must not be misrepresented, either by
  866.  *    explicit claim or by omission.  Since few users ever read sources,
  867.  *    credits must appear in the documentation.
  868.  *
  869.  * 3. Altered versions must be plainly marked as such, and must not be
  870.  *    misrepresented as being the original software.  Since few users
  871.  *    ever read sources, credits must appear in the documentation.
  872.  *
  873.  * 4. This notice may not be removed or altered.
  874.  */
  875.  
  876. #include <stdio.h>
  877. #include "file.h"
  878.  
  879. #ifndef    lint
  880. static char *moduleid = 
  881.     "@(#)$Header: softmagic.c,v 1.7 87/11/06 11:25:31 ian Exp $";
  882. #endif    /* lint */
  883.  
  884. extern char *progname;
  885. extern char *magicfile;    /* name of current /etc/magic or clone */
  886. extern int debug, nmagic;
  887. extern FILE *efopen();
  888. extern struct magic magic[];
  889. static int magindex;
  890.  
  891. /*
  892.  * softmagic - lookup one file in database 
  893.  * (already read from /etc/magic by apprentice.c).
  894.  * Passed the name and FILE * of one file to be typed.
  895.  */
  896. softmagic(buf)
  897. char *buf;
  898. {
  899.     magindex = 0;
  900.     if (match(buf))
  901.         return 1;
  902.  
  903.     return 0;
  904. }
  905.  
  906. /*
  907.  * go through the whole list, stopping if you find a match.
  908.  * Be sure to process every continuation of this match.
  909.  */
  910. match(s)
  911. char    *s;
  912. {
  913.     while (magindex < nmagic) {
  914.         /* if main entry matches, print it... */
  915.         if (mcheck(s, &magic[magindex])) {
  916.             mprint(&magic[magindex],s);
  917.             /* and any continuations that match */
  918.             while (magic[magindex+1].contflag &&
  919.                 magindex < nmagic) {
  920.                 ++magindex;
  921.                 if (mcheck(s, &magic[magindex])){
  922.                     (void) putchar(' ');
  923.                     mprint(&magic[magindex],s);
  924.                 }
  925.             }
  926.             return 1;        /* all through */
  927.         } else {
  928.             /* main entry didn't match, flush its continuations */
  929.             while (magic[magindex+1].contflag &&
  930.                 magindex < nmagic) {
  931.                 ++magindex;
  932.             }
  933.         }
  934.         ++magindex;            /* on to the next */
  935.     }
  936.     return 0;                /* no match at all */
  937. }
  938.  
  939.  
  940. mprint(m,s)
  941. struct magic *m;
  942. char *s;
  943. {
  944.     register union VALUETYPE *p = (union VALUETYPE *)(s+m->offset);
  945.     char *pp, *strchr();
  946.  
  947.     switch (m->type) {
  948.     case BYTE:
  949.         (void) printf(m->desc, p->b);
  950.         break;
  951.     case SHORT:
  952.         (void) printf(m->desc, p->h);
  953.         break;
  954.     case LONG:
  955.         (void) printf(m->desc, p->l);
  956.         break;
  957.     case STRING:
  958.         if ((pp=strchr(p->s, '\n')) != NULL)
  959.             *pp = '\0';
  960.         (void) printf(m->desc, p->s);
  961.         break;
  962.     default:
  963.         warning("invalid m->type (%d) in mprint()", m->type);
  964.     }
  965. }
  966.  
  967. int
  968. mcheck(s, m)
  969. char    *s;
  970. struct magic *m;
  971. {
  972.     register union VALUETYPE *p = (union VALUETYPE *)(s+m->offset);
  973.     register long l = m->value.l;
  974.     register long v;
  975.  
  976.     if (debug) {
  977.         (void) printf("mcheck: %10.10s ", s);
  978.         mdump(m);
  979.     }
  980.     switch (m->type) {
  981.     case BYTE:
  982.         v = p->b; break;
  983.     case SHORT:
  984.         v = p->h; break;
  985.     case LONG:
  986.         v = p->l; break;
  987.     case STRING:
  988.         l = 0;
  989.         /* What we want here is:
  990.          * v = strncmp(m->value.s, p->s, m->vallen);
  991.          * but ignoring any nulls.  bcmp doesn't give -/+/0
  992.          * and isn't universally available anyway.
  993.          */
  994.         {
  995.             register unsigned char *a = (unsigned char*)m->value.s;
  996.             register unsigned char *b = (unsigned char*)p->s;
  997.             register int len = m->vallen;
  998.  
  999.             while (--len >= 0)
  1000.                 if ((v = *b++ - *a++) != 0)
  1001.                     break;
  1002.         }
  1003.         break;
  1004.     default:
  1005.         warning("invalid type %d in mcheck()", m->type);
  1006.         return 0;
  1007.     }
  1008.  
  1009.     switch (m->reln) {
  1010.     case '=':
  1011.         return v == l;
  1012.     case '>':
  1013.         return v > l;
  1014.     case '<':
  1015.         return v < l;
  1016.     case '&':
  1017.         return v & l;
  1018.     default:
  1019.         warning("mcheck: can't happen: invalid relation %d", m->reln);
  1020.         return 0;
  1021.     }
  1022. }
  1023. @@@End of softmagic.c
  1024. echo x - ascmagic.c 1>&2
  1025. cat >ascmagic.c <<'@@@End of ascmagic.c'
  1026. /*
  1027.  * Ascii magic -- file types that we know based on keywords
  1028.  * that can appear anywhere in the file.
  1029.  *
  1030.  * Copyright (c) Ian F. Darwin, 1987.
  1031.  * Written by Ian F. Darwin.
  1032.  *
  1033.  * This software is not subject to any license of the American Telephone
  1034.  * and Telegraph Company or of the Regents of the University of California.
  1035.  *
  1036.  * Permission is granted to anyone to use this software for any purpose on
  1037.  * any computer system, and to alter it and redistribute it freely, subject
  1038.  * to the following restrictions:
  1039.  *
  1040.  * 1. The author is not responsible for the consequences of use of this
  1041.  *    software, no matter how awful, even if they arise from flaws in it.
  1042.  *
  1043.  * 2. The origin of this software must not be misrepresented, either by
  1044.  *    explicit claim or by omission.  Since few users ever read sources,
  1045.  *    credits must appear in the documentation.
  1046.  *
  1047.  * 3. Altered versions must be plainly marked as such, and must not be
  1048.  *    misrepresented as being the original software.  Since few users
  1049.  *    ever read sources, credits must appear in the documentation.
  1050.  *
  1051.  * 4. This notice may not be removed or altered.
  1052.  */
  1053.  
  1054. #include <stdio.h>
  1055. #include <ctype.h>
  1056. #include "file.h"
  1057. #include "names.h"
  1058.  
  1059. #ifndef    lint
  1060. static char *moduleid = 
  1061.     "@(#)$Header: ascmagic.c,v 1.5 87/09/16 14:44:45 ian Exp $";
  1062. #endif    /* lint */
  1063.  
  1064. char *ckfmsg = "write error on output";
  1065.  
  1066.             /* an optimisation over plain strcmp() */
  1067. #define    STREQ(a, b)    (*(a) == *(b) && strcmp((a), (b)) == 0)
  1068.  
  1069. ascmagic(buf)
  1070. register char    *buf;
  1071. {
  1072.     register int i;
  1073.     char    *s, *strtok(), *token;
  1074.     register struct names *p;
  1075.     extern int nbytes;
  1076.     short has_escapes = 0;
  1077.  
  1078.     /* these are easy, do them first */
  1079.  
  1080.     /*
  1081.      * for troff, look for . + letter + letter;
  1082.      * this must be done to disambiguate tar archives' ./file
  1083.      * and other trash from real troff input.
  1084.      */
  1085.     if (*buf == '.' && 
  1086.         isascii(*(buf+1)) && isalnum(*(buf+1)) &&
  1087.         isascii(*(buf+2)) && isalnum(*(buf+2))){
  1088.         ckfputs("troff or preprocessor input text", stdout);
  1089.         return 1;
  1090.     }
  1091.     if ((*buf == 'c' || *buf == 'C') && 
  1092.         isascii(*(buf + 1)) && isspace(*(buf + 1))) {
  1093.         ckfputs("fortran program text", stdout);
  1094.         return 1;
  1095.     }
  1096.  
  1097.     /* look for tokens from names.h - this is expensive! */
  1098.     s = buf;
  1099.     while ((token = strtok(s, " \t\n\r\f")) != NULL) {
  1100.         s = NULL;    /* make strtok() keep on tokin' */
  1101.         for (p = names; p < names + NNAMES; p++) {
  1102.             if (STREQ(p->name, token)) {
  1103.                 ckfputs(types[p->type], stdout);
  1104.                 return 1;
  1105.             }
  1106.         }
  1107.     }
  1108.  
  1109.     switch (is_tar(buf)) {
  1110.     case 1:
  1111.         ckfputs("tar archive", stdout);
  1112.         return 1;
  1113.     case 2:
  1114.         ckfputs("POSIX tar archive", stdout);
  1115.         return 1;
  1116.     }
  1117.  
  1118.     for (i = 0; i < nbytes; i++) {
  1119.         if (!isascii(*(buf+i)))
  1120.             return 0;    /* not all ascii */
  1121.         if (*(buf+i) == '\033')    /* ascii ESCAPE */
  1122.             has_escapes ++;
  1123.     }
  1124.  
  1125.     /* all else fails, but it is ascii... */
  1126.     if (has_escapes){
  1127.         ckfputs("ascii text (with escape sequences)", stdout);
  1128.         }
  1129.     else {
  1130.         ckfputs("ascii text", stdout);
  1131.         }
  1132.     return 1;
  1133. }
  1134.  
  1135.  
  1136. @@@End of ascmagic.c
  1137. echo x - is_tar.c 1>&2
  1138. cat >is_tar.c <<'@@@End of is_tar.c'
  1139. /*
  1140.  * is_tar() -- figure out whether file is a tar archive.
  1141.  *
  1142.  * Stolen (by the author!) from the public domain tar program:
  1143.  * Pubic Domain version written 26 Aug 1985 John Gilmore (ihnp4!hoptoad!gnu).
  1144.  *
  1145.  * @(#)list.c 1.18 9/23/86 Public Domain - gnu
  1146.  *
  1147.  * Comments changed and some code/comments reformatted
  1148.  * for file command by Ian Darwin.
  1149.  */
  1150.  
  1151. #include <ctype.h>
  1152. #include <sys/types.h>
  1153. #include "tar.h"
  1154.  
  1155. #define    isodigit(c)    ( ((c) >= '0') && ((c) <= '7') )
  1156.  
  1157. long from_oct();            /* Decode octal number */
  1158.  
  1159. /*
  1160.  * Return 
  1161.  *    0 if the checksum is bad (i.e., probably not a tar archive), 
  1162.  *    1 for old UNIX tar file,
  1163.  *    2 for Unix Std (POSIX) tar file.
  1164.  */
  1165. int
  1166. is_tar(header)
  1167.     register union record *header;
  1168. {
  1169.     register int    i;
  1170.     register long    sum, recsum;
  1171.     register char    *p;
  1172.  
  1173.     recsum = from_oct(8,  header->header.chksum);
  1174.  
  1175.     sum = 0;
  1176.     p = header->charptr;
  1177.     for (i = sizeof(*header); --i >= 0;) {
  1178.         /*
  1179.          * We can't use unsigned char here because of old compilers,
  1180.          * e.g. V7.
  1181.          */
  1182.         sum += 0xFF & *p++;
  1183.     }
  1184.  
  1185.     /* Adjust checksum to count the "chksum" field as blanks. */
  1186.     for (i = sizeof(header->header.chksum); --i >= 0;)
  1187.         sum -= 0xFF & header->header.chksum[i];
  1188.     sum += ' '* sizeof header->header.chksum;    
  1189.  
  1190.     if (sum != recsum)
  1191.         return 0;    /* Not a tar archive */
  1192.     
  1193.     if (0==strcmp(header->header.magic, TMAGIC)) 
  1194.         return 2;        /* Unix Standard tar archive */
  1195.  
  1196.     return 1;            /* Old fashioned tar archive */
  1197. }
  1198.  
  1199.  
  1200. /*
  1201.  * Quick and dirty octal conversion.
  1202.  *
  1203.  * Result is -1 if the field is invalid (all blank, or nonoctal).
  1204.  */
  1205. long
  1206. from_oct(digs, where)
  1207.     register int    digs;
  1208.     register char    *where;
  1209. {
  1210.     register long    value;
  1211.  
  1212.     while (isspace(*where)) {        /* Skip spaces */
  1213.         where++;
  1214.         if (--digs <= 0)
  1215.             return -1;        /* All blank field */
  1216.     }
  1217.     value = 0;
  1218.     while (digs > 0 && isodigit(*where)) {    /* Scan til nonoctal */
  1219.         value = (value << 3) | (*where++ - '0');
  1220.         --digs;
  1221.     }
  1222.  
  1223.     if (digs > 0 && *where && !isspace(*where))
  1224.         return -1;            /* Ended on non-space/nul */
  1225.  
  1226.     return value;
  1227. }
  1228. @@@End of is_tar.c
  1229. echo x - print.c 1>&2
  1230. cat >print.c <<'@@@End of print.c'
  1231. /*
  1232.  * print.c - debugging printout routines
  1233.  *
  1234.  * Copyright (c) Ian F. Darwin, 1987.
  1235.  * Written by Ian F. Darwin.
  1236.  *
  1237.  * This software is not subject to any license of the American Telephone
  1238.  * and Telegraph Company or of the Regents of the University of California.
  1239.  *
  1240.  * Permission is granted to anyone to use this software for any purpose on
  1241.  * any computer system, and to alter it and redistribute it freely, subject
  1242.  * to the following restrictions:
  1243.  *
  1244.  * 1. The author is not responsible for the consequences of use of this
  1245.  *    software, no matter how awful, even if they arise from flaws in it.
  1246.  *
  1247.  * 2. The origin of this software must not be misrepresented, either by
  1248.  *    explicit claim or by omission.  Since few users ever read sources,
  1249.  *    credits must appear in the documentation.
  1250.  *
  1251.  * 3. Altered versions must be plainly marked as such, and must not be
  1252.  *    misrepresented as being the original software.  Since few users
  1253.  *    ever read sources, credits must appear in the documentation.
  1254.  *
  1255.  * 4. This notice may not be removed or altered.
  1256.  */
  1257.  
  1258. #include <stdio.h>
  1259. #include <errno.h>
  1260. #include "file.h"
  1261.  
  1262. #ifndef    lint
  1263. static char *moduleid = 
  1264.     "@(#)$Header: print.c,v 1.11 88/01/15 12:17:06 ian Exp $";
  1265. #endif    /* lint */
  1266.  
  1267. #define MAXSTR        500
  1268.  
  1269. extern char *progname;
  1270. extern char *magicfile;
  1271. extern int debug, nmagic;    /* number of valid magic[]s */
  1272. extern void showstr();
  1273.  
  1274. mdump(m)
  1275. struct magic *m;
  1276. {
  1277.     (void) printf("%d\t%d\t%d\t%c\t",
  1278.         m->contflag,
  1279.         m->offset,
  1280.         m->type,
  1281.         m->reln,
  1282.         0);
  1283.     if (m->type == STRING)
  1284.         showstr(m->value.s);
  1285.     else
  1286.         (void) printf("%d",m->value.l);
  1287.     (void) printf("\t%s", m->desc);
  1288.     (void) putchar('\n');
  1289. }
  1290.  
  1291. /*
  1292.  * error - print best error message possible and exit
  1293.  */
  1294. /*ARGSUSED1*/
  1295. /*VARARGS*/
  1296. void
  1297. error(s1, s2)
  1298. char *s1, *s2;
  1299. {
  1300.     warning(s1, s2);
  1301.     exit(1);
  1302. }
  1303.  
  1304. /*ARGSUSED1*/
  1305. /*VARARGS*/
  1306. warning(f, a)
  1307. char *f, *a;
  1308. {
  1309.     extern int errno, sys_nerr;
  1310.     extern char *sys_errlist[];
  1311.     int myerrno;
  1312.  
  1313.     myerrno = errno;
  1314.  
  1315.     /* cuz we use stdout for most, stderr here */
  1316.     (void) fflush(stdout); 
  1317.  
  1318.     if (progname != NULL) {
  1319.         (void) fputs(progname, stderr);
  1320.         (void) putc(':', stderr);
  1321.         (void) putc(' ', stderr);
  1322.     }
  1323.     (void) fprintf(stderr, f, a);
  1324.     if (myerrno > 0 && myerrno < sys_nerr)
  1325.         (void) fprintf(stderr, " (%s)", sys_errlist[myerrno]);
  1326.     putc('\n', stderr);
  1327. }
  1328. @@@End of print.c
  1329. echo x - getopt.c 1>&2
  1330. cat >getopt.c <<'@@@End of getopt.c'
  1331.  
  1332. /*
  1333.  * getopt - get option letter from argv
  1334.  *
  1335.  * Copyright (c) Henry Spencer.
  1336.  * Written by Henry Spencer.
  1337.  *
  1338.  * This software is not subject to any license of the American Telephone
  1339.  * and Telegraph Company or of the Regents of the University of California.
  1340.  *
  1341.  * Permission is granted to anyone to use this software for any purpose on
  1342.  * any computer system, and to alter it and redistribute it freely, subject
  1343.  * to the following restrictions:
  1344.  *
  1345.  * 1. The author is not responsible for the consequences of use of this
  1346.  *    software, no matter how awful, even if they arise from flaws in it.
  1347.  *
  1348.  * 2. The origin of this software must not be misrepresented, either by
  1349.  *    explicit claim or by omission.  Since few users ever read sources,
  1350.  *    credits must appear in the documentation.
  1351.  *
  1352.  * 3. Altered versions must be plainly marked as such, and must not be
  1353.  *    misrepresented as being the original software.  Since few users
  1354.  *    ever read sources, credits must appear in the documentation.
  1355.  *
  1356.  * 4. This notice may not be removed or altered.
  1357.  */
  1358.  
  1359. /*
  1360.  * changed index() calls to strchr() - darwin, oct 87.
  1361.  */
  1362.  
  1363. #include <stdio.h>
  1364.  
  1365. char    *optarg;    /* Global argument pointer. */
  1366. int    optind = 0;    /* Global argv index. */
  1367.  
  1368. static char    *scan = NULL;    /* Private scan pointer. */
  1369.  
  1370. extern char    *strchr();
  1371.  
  1372. int
  1373. getopt(argc, argv, optstring)
  1374. int argc;
  1375. char *argv[];
  1376. char *optstring;
  1377. {
  1378.     register char c;
  1379.     register char *place;
  1380.  
  1381.     optarg = NULL;
  1382.  
  1383.     if (scan == NULL || *scan == '\0') {
  1384.         if (optind == 0)
  1385.             optind++;
  1386.     
  1387.         if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
  1388.             return(EOF);
  1389.         if (strcmp(argv[optind], "--")==0) {
  1390.             optind++;
  1391.             return(EOF);
  1392.         }
  1393.     
  1394.         scan = argv[optind]+1;
  1395.         optind++;
  1396.     }
  1397.  
  1398.     c = *scan++;
  1399.     place = strchr(optstring, c);
  1400.  
  1401.     if (place == NULL || c == ':') {
  1402.         fprintf(stderr, "%s: unknown option -%c\n", argv[0], c);
  1403.         return('?');
  1404.     }
  1405.  
  1406.     place++;
  1407.     if (*place == ':') {
  1408.         if (*scan != '\0') {
  1409.             optarg = scan;
  1410.             scan = NULL;
  1411.         } else if (optind < argc) {
  1412.             optarg = argv[optind];
  1413.             optind++;
  1414.         } else {
  1415.             fprintf(stderr, "%s: -%c argument missing\n", argv[0], c);
  1416.             return('?');
  1417.         }
  1418.     }
  1419.  
  1420.     return(c);
  1421. }
  1422. @@@End of getopt.c
  1423. echo x - strtol.c 1>&2
  1424. cat >strtol.c <<'@@@End of strtol.c'
  1425. /*
  1426.  * strtol - convert string to long integer.
  1427.  *
  1428.  * Written by reading the System V Interface Definition, not the code.
  1429.  *
  1430.  * Totally public domain.
  1431.  *
  1432.  * Compile with -DTEST to get short interactive main() for testing.
  1433.  */
  1434.  
  1435. #include <stdio.h>
  1436. #include <ctype.h>
  1437.  
  1438. long
  1439. strtol(s, p, b)
  1440. char    *s, **p;
  1441. int    b;
  1442. {
  1443.     int    base = 10, n = 0, sign = 1, valid = 1;
  1444.  
  1445.     /*
  1446.      * leading sign?
  1447.      */
  1448.     if (*s=='-')
  1449.         sign=(-1);
  1450.     else
  1451.         sign=1;
  1452.     if (*s=='+' || *s=='-')
  1453.         ++s; /* skip sign */
  1454.  
  1455.     /*
  1456.      * what base are we really using?
  1457.      */
  1458.     if (b == 0) {
  1459.         if (strncmp(s, "0x", 2) == 0  ||
  1460.             strncmp(s, "0X", 2) == 0) {
  1461.             s += 2;
  1462.             base = 16;
  1463.         } else
  1464.             if (*s == '0')
  1465.                 base = 8;
  1466.     }
  1467.  
  1468.     /*
  1469.      * convert the string to a number.
  1470.      */
  1471.     while (isascii(*s) && valid) {
  1472.         switch(*s) {
  1473.         case '0':
  1474.         case '1':
  1475.         case '2':
  1476.         case '3':
  1477.         case '4':
  1478.         case '5':
  1479.         case '6':
  1480.         case '7':
  1481.             n = base*n  +  *s-'0';
  1482.             break;
  1483.         case '8':
  1484.         case '9':
  1485.             if (base >8)
  1486.                 n = base*n  +  *s-'0';
  1487.             else
  1488.                 valid = 0;
  1489.             break;
  1490.         case 'a':
  1491.         case 'b':
  1492.         case 'c':
  1493.         case 'd':
  1494.         case 'e':
  1495.         case 'f':
  1496.             if (base == 16)
  1497.                 n = base*n + *s-'a'+10;
  1498.             else
  1499.                 valid = 0;
  1500.             break;
  1501.         case 'A':
  1502.         case 'B':
  1503.         case 'C':
  1504.         case 'D':
  1505.         case 'E':
  1506.         case 'F':
  1507.             if (base == 16)
  1508.                 n = base*n + *s-'A'+10;
  1509.             else
  1510.                 valid = 0;
  1511.             break;
  1512.         default:
  1513.             valid = 0;
  1514.             break;
  1515.         }
  1516.         ++s;
  1517.     }
  1518.  
  1519.     /*
  1520.      * if arg `p' is not NULL, a ptr to the character
  1521.      * terminating the scan will be returned in `p'
  1522.      */
  1523.     if (*p != NULL)
  1524.         *p = s;
  1525.  
  1526.     return sign * n;
  1527. }
  1528.  
  1529. #ifdef    TEST
  1530. main(argc, argv)
  1531. int argc;
  1532. char **argv;
  1533. {
  1534.     int i;
  1535.     long j, strtol();
  1536.  
  1537.     for (i=1; i<argc; i++) {
  1538.         j = strtol(argv[i], 0, 0);
  1539.         printf("%s -> %ld(%lx)\n", argv[i], j, j);
  1540.     }
  1541.     exit(0);
  1542. }
  1543. #endif    /*TEST*/
  1544. @@@End of strtol.c
  1545.  
  1546.