home *** CD-ROM | disk | FTP | other *** search
/ back2roots/padua / padua.7z / padua / uucp / unshar-1.4.lzh / src / unshar.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-13  |  19.4 KB  |  817 lines

  1. /*
  2.  *                     Unshar V1.4 (C) Copyright Eddy Carroll 1992
  3.  *                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4.  * Usage: Unshar {-overwrite} {-nosort} <filename> ...
  5.  *
  6.  * Extracts files from a SHAR'd archive.
  7.  *
  8.  * This utility has a few advantages over the version of SH on Fish Disk 92.
  9.  * For a start, it doesn't crash if it gets a slightly unusual format! It
  10.  * also has a (limited) capability for extracting files from shar archives
  11.  * which use 'SED' rather than 'CAT' (typically, this is done so that
  12.  * each line in the file may be prefixed with an 'X' or similar, so that
  13.  * indentation is preserved). Unshar will spot 'SED' lines, and treat them
  14.  * the same as 'CAT' (allowing for different parameters of course) with
  15.  * the exception that any leading characters matching the string specified
  16.  * in the SED command are discarded.
  17.  *
  18.  * Unshar checks files being extracted to see if they are to be stored
  19.  * within a sub-directory. If they are, and the sub-directory does not
  20.  * already exist, it is created.
  21.  *
  22.  * One other small addition is that any filenames which are prefixed with
  23.  * the characters "./" have these characters removed. Some shar files
  24.  * use this prefix to ensure that the files are stored in the current
  25.  * directory.
  26.  *
  27.  * Files are extracted into the current directory. As each file is extracted,
  28.  * an appropriate message is printed on the screen. If the file already
  29.  * exists, the user is warned and given the chance to avoid overwriting it
  30.  * "Overwrite file (Yes/No/All)? ". The default is Yes. If All is selected,
  31.  * then this prompt is supressed for the rest of the current file. It may
  32.  * be disabled for all the files by specifying the -o switch on the
  33.  * command line.
  34.  *
  35.  * By default, unshar will do a `prescan' over all the files listed, looking
  36.  * at the first few lines of each for a Subject: line. If one is found, then
  37.  * it examines it for Issue numbers and Part numbers, and unshars those files
  38.  * having the lowest numbers first. This results in the shar files being
  39.  * extracted in the correct order, regardless of what order they were listed
  40.  * in on the command line. You can override this behaviour and unshar files
  41.  * in the command line order by specifying the -n switch.
  42.  * 
  43.  * DISTRIBUTION
  44.  * I retain copyright rights to this source code, though it may be _freely_
  45.  * distributed. The executable file created from this source code is in
  46.  * the Public Domain and may be distributed without any restrictions.
  47.  *
  48.  *
  49.  * N.b. The code is starting to look a bit messy; could be it will get
  50.  *      a complete overhaul for the next revision.
  51.  *
  52.  */
  53.  
  54. /* Compiles under Lattice V5.04 */
  55.  
  56. #ifndef LATTICE_50 
  57. #include "system.h"
  58. #endif
  59.  
  60. #define YES            1
  61. #define NO            0
  62. #define CR            '\015'
  63. #define EOL            '\012'
  64. #define SINGLEQUOTE '\''
  65. #define DOUBLEQUOTE '\042'
  66. #define MAXSTRING    512        /* Maximum length of input line */
  67.  
  68. /*
  69.  *        New handler for Ctrl-C. Checks if CTRL-C received, and if it has,
  70.  *        sets the global CtrlC variable to true.
  71.  */
  72. #define chkabort() (CtrlC |= ((SetSignal(0,0) & SIGBREAKF_CTRL_C)))
  73.  
  74.  
  75. char HelpMsg[] = "\
  76. Unshar V1.4 by Eddy Carroll 1992 Public Domain, extracts Unix shar archives.\
  77. \n\
  78. Usage: unshar {-overwrite} {-nosort} <filename> ...\n";
  79.  
  80. char DiskMsg[]  = "Unshar aborted - Disk write error (disk full?)\n";
  81. char ErrorMsg[] = "Unshar: Invalid CAT or SED command at line ";
  82.  
  83. int linenum;
  84. int CtrlC = NO;
  85.  
  86. char Version[] = "$VER: Unshar 1.4 (29.9.92)";
  87.  
  88. /*
  89.  * --------------------------------------------------------------------------
  90.  * The following block may be removed `as-is' and used in other programs.
  91.  * It provides basic buffered i/o on two files, an input file and an output
  92.  * file. It also provides output to the current standard output via
  93.  * print. Buffering is done using buffers of size MAXBUF.
  94.  *
  95.  * The following routines are provided:
  96.  *
  97.  * getc() returns an integer corresponding to the next character read from
  98.  * infile, or EOF if the end of file has been reached.
  99.  *
  100.  * putc(c) outputs a character to outfile. If a diskerror occurs, the global
  101.  * diskerror is set to YES, and all further diskwrites are ignored.
  102.  *
  103.  * getline() returns a pointer to a string containing the next line
  104.  * read in from infile. getline() also checks for CTRL-C via chkabort()
  105.  * 
  106.  * putline(s) outputs a string to outfile, returning non-zero if an
  107.  * error occurred during the write.
  108.  *
  109.  * flushin() resets getc() and getline() for input from a new file
  110.  *
  111.  * flushout() flushes output buffer; call prior to closing output file.
  112.  *
  113.  * input() returns a pointer to a string containing a line from stdin.
  114.  *
  115.  * print(s) prints a message on standard output.
  116.  *
  117.  * print3(s1,s2,s3) outputs three strings on standard output.
  118.  *
  119.  * numtostr(n) returns a pointer to the ascii representation of n.
  120.  *
  121.  * Special Notes
  122.  * ~~~~~~~~~~~~~
  123.  * You should ensure that you use the filenames 'infile' and 'outfile'
  124.  * when you are opening the input and output files in main(). Also,
  125.  * do not #define EOF or MAXBUF elsewhere in your program.
  126.  *
  127.  */
  128.  
  129. #define EOF        -1
  130. #define MAXBUF    10000
  131.  
  132. BPTR infile, outfile;
  133. LONG maxin = MAXBUF, maxout = MAXBUF, inbuf = MAXBUF, outbuf = 0;
  134. unsigned char inbuffer[MAXBUF], outbuffer[MAXBUF];
  135. int diskerror = NO;
  136.  
  137. /*
  138.  *        int getc()
  139.  *        ----------
  140.  *        Returns next character from infile, or EOF if end of file.
  141.  *
  142.  *        Replaced by a macro to improve performance. Original function was:
  143.  *
  144.  *        int getc()
  145.  *        {
  146.  *            if (!maxin)
  147.  *                return (EOF);
  148.  *
  149.  *            if (inbuf >= maxin) {
  150.  *                maxin = Read(infile, inbuffer, MAXBUF);
  151.  *                inbuf = 0;
  152.  *                if (!maxin)
  153.  *                    return (EOF);
  154.  *            }
  155.  *            return (inbuffer[inbuf++]);
  156.  *        }
  157.  *
  158.  */
  159. #define IF(x,y,z) ((x) ? (y) : (z))
  160.  
  161. #define getc()    IF(!maxin, EOF, \
  162.                 IF(inbuf >= maxin, ( \
  163.                     inbuf = 0, maxin = Read(infile, inbuffer, MAXBUF), \
  164.                     IF(!maxin, EOF, inbuffer[inbuf++]) \
  165.                 ), inbuffer[inbuf++])) \
  166.  
  167. /* 
  168.  *        Prepares getc() for input from a new file
  169.  */
  170. #define flushin() (maxin = MAXBUF, inbuf = MAXBUF)
  171.  
  172. /*
  173.  *        putc(ch)
  174.  *        --------
  175.  *        Outputs character ch to disk. If a diskerror is detected, then all
  176.  *        further output is ignored and the global diskerror is set to YES.
  177.  *
  178.  *        Replaced by a macro for performance reasons. Original function was:
  179.  *
  180.  *        void putc(ch)
  181.  *        int ch;
  182.  *        {
  183.  *            if (ch == EOF)
  184.  *                maxout = outbuf;
  185.  *            else
  186.  *                outbuffer[outbuf++] = ch;
  187.  *        
  188.  *            if (outbuf >= maxout) {
  189.  *                if (!diskerror && Write(outfile, outbuffer, maxout) == -1)
  190.  *                    diskerror = YES;
  191.  *                outbuf = 0;
  192.  *                maxout = MAXBUF;
  193.  *            }
  194.  *        }
  195.  */
  196. #define flushout() (maxout = outbuf, \
  197.                     IF(!diskerror && Write(outfile, outbuffer, maxout) == -1, \
  198.                         diskerror = YES, \
  199.                         0), \
  200.                     outbuf = 0, maxout = MAXBUF)
  201.  
  202. #define putc(ch) (outbuffer[outbuf++] = ch, \
  203.                   IF(outbuf >= maxout, \
  204.                       (IF (!diskerror && \
  205.                             Write(outfile, outbuffer, maxout) == -1, \
  206.                         diskerror = YES, \
  207.                         0), \
  208.                      outbuf = 0, maxout = MAXBUF), \
  209.                     0))
  210.  
  211. /*
  212.  *        print(s)
  213.  *        --------
  214.  *        Outputs a message to std output
  215.  */
  216. void print(s)
  217. char *s;
  218. {
  219.     Write(Output(),s,strlen(s));
  220. }
  221.  
  222. /*
  223.  *        print3()
  224.  *        --------
  225.  *        Outputs three strings to std output.
  226.  *        Useful for sequences like print3("string", variable, "string");
  227.  */
  228. void print3(s1,s2,s3)
  229. char *s1,*s2,*s3;
  230. {
  231.     print(s1);
  232.     print(s2);
  233.     print(s3);
  234. }
  235.  
  236. /*
  237.  *        getline()
  238.  *        ---------
  239.  *        Reads in a line from current infile into string, and returns a
  240.  *        pointer to that string. Returns NULL if EOF encountered.
  241.  */
  242. char *getline()
  243. {
  244.     register int ch, i = 0;
  245.     static char line[MAXSTRING];
  246.  
  247.     ch = getc();
  248.     if (ch == EOF)
  249.         return (NULL);
  250.  
  251.     while (i < (MAXSTRING-1) && ch != EOF && ch != EOL) {
  252.         line[i++] = ch;
  253.         ch = getc();
  254.     }
  255.  
  256.     line[i] = '\0';
  257.     linenum++;
  258.     chkabort();
  259.     return (line);
  260. }
  261.  
  262. /*
  263.  *        putline()
  264.  *        ---------
  265.  *        Outputs a string to the current output file (terminating it with LF).
  266.  *        Returns 0 for success, non-zero for disk error
  267.  */
  268. int putline(s)
  269. char *s;
  270. {
  271.     while (*s)
  272.         putc(*s++);
  273.     putc(EOL);
  274.     return (diskerror);
  275. }
  276.  
  277. /*
  278.  *        input()
  279.  *        -------
  280.  *        Reads a line from keyboard and returns pointer to it
  281.  */
  282. char *input()
  283. {
  284.     static char s[80];
  285.     int len;
  286.  
  287.     s[0] = '\0';
  288.     len = Read(Input(),s,75);
  289.     if (len < 0)
  290.         len = 0;
  291.     s[len] = '\0';
  292.     chkabort();
  293.     return(s);
  294. }
  295.  
  296. /*
  297.  *        numtostr()
  298.  *        ----------
  299.  *        Converts integer to string and returns pointer to it.
  300.  */
  301. char *numtostr(n)
  302. int n;
  303. {
  304.     static char s[20];
  305.     int i = 19;
  306.  
  307.     s[19] = '\0';
  308.     if (n)
  309.         while (n)
  310.             s[--i] = '0' + (n % 10), n /= 10;
  311.     else
  312.         s[--i] = '0';
  313.     return(&s[i]);
  314. }
  315.  
  316. /*
  317.  *        --------------------* End of Buffered IO routines *-----------------
  318.  */
  319.  
  320. /*
  321.  *        Skips spaces at the start of a string
  322.  */
  323. char *SkipBlanks(char *buf)
  324. {
  325.     if (buf) {
  326.         while (*buf && isspace(*buf))
  327.             buf++;
  328.     }
  329.     return(buf);
  330. }
  331.  
  332. /*
  333.  *        index()
  334.  *        -------
  335.  *        Like standard Unix index(), but skips over quotes if skip == true.
  336.  *        Also skips over chars prefixed by a \. Returns pointer to first
  337.  *        occurance of char c inside string s, or NULL.
  338.  */
  339. char *index(s,c,skip)
  340. char *s,c;
  341. int skip;
  342. {
  343.     register char *p = s;
  344.     register int noquotes = YES, literal = NO;
  345.  
  346.     while (*p) {
  347.         if (literal) {
  348.             literal = NO;
  349.             p++;
  350.         } else {
  351.             if (skip && ((*p == SINGLEQUOTE) || (*p == DOUBLEQUOTE)))
  352.                 noquotes = !noquotes;
  353.             if (noquotes && (*p == c))
  354.                 return(p);
  355.             literal = (*p == '\\');
  356.             p++;
  357.         }
  358.     }
  359.     return (NULL);
  360. }
  361.  
  362. /*
  363.  *        getname()
  364.  *        ---------
  365.  *        Extracts a string from start of string s1 and stores it in s2.
  366.  *        Leading spaces are discarded, and quotes, if present, are used to
  367.  *        indicate the start and end of the filename. If mode is MODE_FILE,
  368.  *        then if the name starts with either './' or '/', this prefix is
  369.  *        stripped. This doesn't happen if the mode is MODE_TEXT. A pointer
  370.  *        to the first character after the string in s1 is returned. In
  371.  *        addition, any characters prefixed with are passed through without
  372.  *        checking.
  373.  */
  374.  
  375. #define MODE_FILE 1
  376. #define MODE_TEXT 2
  377.  
  378. char *getname(s1,s2,mode)
  379. char *s1,*s2;
  380. {
  381.     char endchar = ' ';
  382.  
  383.     while (*s1 == ' ')
  384.         s1++;
  385.  
  386.     if (*s1 == SINGLEQUOTE || *s1 == DOUBLEQUOTE)
  387.         endchar = *s1++;
  388.  
  389.     if (mode == MODE_FILE) {
  390.         if (s1[0] == '.' && s1[1] == '/')
  391.             s1 += 2;
  392.         while (*s1 == '/')
  393.             s1++;
  394.     }
  395.  
  396.     while (*s1 && *s1 != endchar) {
  397.         if (*s1 == '\\' && *(s1+1))
  398.             s1++;
  399.         *s2++ = *s1++;
  400.     }
  401.     *s2 = '\0';
  402.  
  403.     if (*s1 == endchar)
  404.         return(++s1);
  405.     else
  406.         return(s1);
  407. }
  408.  
  409.  
  410. /*
  411.  *        checkfordir()
  412.  *        -------------
  413.  *        Checks filename to see if it is inside a subdirectory. If it is,
  414.  *        then checks if subdirectory exists, and creates it if it doesn't.
  415.  */
  416. void checkfordir(filename)
  417. char *filename;
  418. {
  419.     char dir[80], *p;
  420.     int i, x;
  421.     BPTR dirlock;
  422.  
  423.     p = filename;
  424.  
  425.     while (p = index(p, '/', 1)) {
  426.  
  427.         /* Dir exists, so copy dir part of filename into dir name area */
  428.  
  429.         x = p - filename;
  430.         for (i = 0; i < x; i++)
  431.             dir[i] = filename[i];
  432.         dir[i] = '\0';
  433.  
  434.         /* Now, see if directory exists, if not then create */
  435.         if ((dirlock = Lock(dir,ACCESS_READ)) == NULL) {
  436.             dirlock = CreateDir(dir);
  437.             if (dirlock) {
  438.                 print3("Creating directory ", dir, "\n");
  439.             }
  440.         }
  441.         if (dirlock)
  442.             UnLock(dirlock);
  443.  
  444.         p++;
  445.     }
  446. }
  447.  
  448. /*
  449.  *        unshar()
  450.  *        --------
  451.  *        Extracts all stored files from a shar file. Returns zero for success,
  452.  *        non-zero if a disk error occurred. If echofilename is non-zero, then
  453.  *        the name of each shar file is output before unsharing it. If
  454.  *        overwrite is non-zero, then existing files are overwritten without
  455.  *        any warning. If title is non-NULL, then it points to a string
  456.  *        which is printed out before any files are extracted.
  457.  */
  458. int unshar(sharfile, title, echofilename, overwrite)
  459. char *sharfile, *title;
  460. int echofilename, overwrite;
  461. {
  462.     register char *s, *p;
  463.     char endmarker[100], filename[100],sedstring[100];
  464.     int endlen, stripfirst, startfile, found = NO, err = NO, skip, sedlen;
  465.     int append;
  466.     BPTR filelock;
  467.  
  468.     if ((infile = Open(sharfile, MODE_OLDFILE)) == NULL) {
  469.         print3("Can't open file ", sharfile, " for input\n");
  470.         return(1);
  471.     }
  472.  
  473.     linenum = 0;
  474.     if (echofilename)
  475.         print3("\033[7m Shar file: ", sharfile, " \033[0m\n");
  476.     if (title)
  477.         print(title);
  478.  
  479.     while (!err && !CtrlC && (s = getline()) != NULL) {
  480.         s = SkipBlanks(s);
  481.         startfile = NO;
  482.         if (strncmp(s,"cat ",4) == 0) {
  483.             startfile  = YES;
  484.             stripfirst = NO;
  485.         }
  486.         if (strncmp(s,"sed ",4) == 0) {
  487.             startfile  = YES;
  488.             stripfirst = YES;
  489.             sedlen = 0;
  490.             /*
  491.              *        Note - tons of sanity checks done here to ensure that a
  492.              *        sed line of the form:
  493.              *
  494.              *            sed >s/somefile <<'endmarker' -e 's/X//'
  495.              *
  496.              *        Will be interpreted correctly.
  497.              */
  498.  
  499. #define ISPREFIX(ch)    (ch == DOUBLEQUOTE || ch == SINGLEQUOTE || ch == ' ')
  500. #define ISMETA(ch)        (ch == '<' || ch == '>')
  501. #define ISOK(s)            (s[1] == '/' && ISPREFIX(s[-1]) && !ISMETA(s[-2]))
  502.  
  503.             p = s;
  504.             while ((p = index(p,'s',0)) != NULL && !ISOK(p))
  505.                 p++;
  506.             if (p != NULL) {
  507.                 p += 2;                /* Skip over the 's/' bit    */
  508.                 if (*p == '^')        /* Skip past starting char    */
  509.                     p++;
  510.                 while (*p && *p != '/')
  511.                     sedstring[sedlen++] = *p++;
  512.             } 
  513.         }
  514.  
  515.         if (startfile) {    
  516.             if (found == NO) {
  517.                 found = YES;
  518.             }
  519.             if ((p = index(s,'>',1)) == NULL) {
  520.                 print3(ErrorMsg, numtostr(linenum), "(a)\n");
  521.             } else {
  522.                 /*
  523.                  *       This next bit checks to see if we are creating or
  524.                  *       appending to the output file (i.e. >file or >>file)
  525.                  */
  526.                 if (*++p == '>') {
  527.                     p++;
  528.                     append = YES;
  529.                 } else
  530.                     append = NO;
  531.                 getname(p,filename,MODE_FILE);
  532.                 p = s;
  533.                 while ((p = index(p,'<',1)) && (p[1] != '<'))
  534.                     ;
  535.                 if (p)
  536.                     getname(p+2,endmarker,MODE_TEXT);
  537.  
  538.                 endlen = strlen(endmarker);
  539.  
  540.                 if (strlen(filename) && endlen) {
  541.  
  542.                     checkfordir(filename);
  543.  
  544.                     /* Found a valid line so perform extract */
  545.  
  546.                     /* Check if file exists */
  547.  
  548.                     skip = NO;
  549.                     outfile = NULL;
  550.                     if (!overwrite) {
  551.                         filelock = Lock(filename, ACCESS_READ);
  552.                         if (filelock) {
  553.                             UnLock(filelock);
  554.                             if (!append) {
  555.                                 print3("Overwrite file ", filename,
  556.                                     " (Yes, [No], All)? ");
  557.  
  558.                                 switch (tolower(*input())) {
  559.                                     case 'a': overwrite = YES;    break;
  560.                                     case 'y': skip = NO;        break;
  561.                                     default : skip = YES;        break;
  562.                                 }
  563.                             }
  564.                         }
  565.                     }
  566.  
  567.                     /*
  568.                      *        Open as old file and seek to the end if
  569.                      *        appending AND the file already exists. If
  570.                      *        it doesn't exist, then just open as new file.
  571.                      */
  572.                     if (filelock && append) {
  573.                         outfile = Open(filename, MODE_READWRITE);
  574.                         if (outfile)
  575.                             Seek(outfile, 0, OFFSET_END);
  576.                     } else if (!skip)
  577.                         outfile = Open(filename, MODE_NEWFILE);
  578.  
  579.                     if (!outfile && !skip) {
  580.                         print3("Couldn't open file ",filename," for output\n");
  581.                         skip = YES;
  582.                     }
  583.                     if (!skip) {
  584.                         if (filelock && append)
  585.                             print3("Extending file ", filename, "\n");
  586.                         else
  587.                             print3("Unsharing file ", filename, "\n");
  588.                     }
  589.                     s = getline();
  590.                     err = NO;
  591.                     while (s && strncmp(s,endmarker,endlen) && !CtrlC) {
  592.                         if (stripfirst && !strncmp(sedstring,s,sedlen))
  593.                             s += sedlen;
  594.                         if (!skip && (err = putline(s)))
  595.                             break;
  596.                         s = getline();
  597.                     }
  598.                     if (!skip) {
  599.                         flushout();
  600.                         if (err || diskerror)
  601.                             print(DiskMsg), err = YES;
  602.                         Close(outfile);
  603.                     }
  604.                 } else
  605.                     print(ErrorMsg, numtostr(linenum), "\n");
  606.             }
  607.         }
  608.     }
  609.  
  610.     if (!err && !CtrlC)
  611.         if (found)
  612.             print("Unshar done\n");
  613.         else
  614.             print("No files to unshar\n");
  615.     Close(infile);
  616.     flushin();
  617.     return(err);
  618. }
  619.  
  620. /*
  621.  *        readheader()
  622.  *        ------------
  623.  *        Reads in the first few lines (actually 480 bytes) of filename, and
  624.  *        scans for a subject line. If the subject line is found, then
  625.  *        it is stored in subject (up to 100 chars in length), else a null
  626.  *        string is stored. The subject line is also examined, and a sequence
  627.  *        number determined. If the subject line starts with i or I, followed
  628.  *        by a number, then this is taken as the sequence number. Otherwise,
  629.  *        the first number after `Part' or `part' is uses. This sequence
  630.  *        number is returned in seqnum. I-type sequence numbers have 1000
  631.  *        added on to them first of all, to keep them seperated from 'part'
  632.  *        types.
  633.  *
  634.  *        The idea is that successive parts of a set of several shar files
  635.  *        will have increasing sequence numbers.
  636.  *
  637.  *        Zero is returned if an error occurred.
  638.  */
  639. int readheader(filename, subject, seqnum)
  640. char *filename, *subject;
  641. int *seqnum;
  642. {
  643.     static char buf[480];
  644.     BPTR file;
  645.     int len, i;
  646.     char *p;
  647.  
  648.     *subject = '\0';
  649.  
  650.     file = Open(filename, MODE_OLDFILE);
  651.     if (!file) {
  652.         print3("Can't open file ", filename, " for input\n");
  653.         return (0);
  654.     }
  655.  
  656.     len = Read(file, buf, 480);
  657.     Close(file);
  658.     if (len == -1) {
  659.         print3("Error reading header from file ", filename, "\n");
  660.         return (0);
  661.     }
  662.  
  663.     /*
  664.      *        Now analyse file for a Subject: line
  665.      */
  666.     for (i = 0; i < len; i++) {
  667.         if (buf[i] == '\n' && strnicmp(&buf[i+1], "Subject:", 8) == 0) {
  668.             /*
  669.              *        Copy subject line into subject string, ensuring
  670.              *        it is properly terminated with a \n and \0
  671.              */
  672.             i++;
  673.             strncpy(subject, buf + i + 9, 98);
  674.             subject[98] = '\0';
  675.             for (p = subject; *p; p++) {
  676.                 if (*p == '\n') {
  677.                     break;
  678.                 }
  679.             }
  680.             *p++ = '\n';
  681.             *p = '\0';
  682.             /*
  683.              *        Now scan new subject string looking for sequence number
  684.              */
  685.             p = subject;
  686.             while (*p) {
  687.                 if (*p == 'i') {
  688.                     *seqnum = atoi(p+1);
  689.                     if (*seqnum != 0)
  690.                         return 1;
  691.                 }
  692.                 if (strnicmp(p, "Part", 4) == 0) {
  693.                     p += 4;
  694.                     while (*p == ' ')
  695.                         *p++;
  696.                     *seqnum = atoi(p);
  697.                     if (*seqnum != 0) {
  698.                         *seqnum += 1000;
  699.                         return 1;
  700.                     }
  701.                 } else
  702.                     p++;
  703.             }
  704.             *seqnum = 10000;
  705.             return (1);
  706.         }
  707.     }
  708.     *seqnum = 10000;
  709.     return (1);
  710. }
  711.  
  712. /*
  713.  *        Start of mainline
  714.  */
  715. int main(argc,argv)
  716. int argc;
  717. char *argv[];
  718. {
  719.  
  720.     int i, numfiles;
  721.     int overwrite = NO, sortfiles = YES;
  722.     char **filenames;
  723.  
  724.     if ((argc == 1) || (*argv[1] == '?')) {
  725.         print(HelpMsg);
  726.         return (10);
  727.     }
  728.     while (argc > 1 && *argv[1] == '-') {
  729.         switch (tolower(argv[1][1])) {
  730.  
  731.             case 'o':
  732.                 overwrite = YES;
  733.                 break;
  734.  
  735.             case 'n':
  736.                 sortfiles = NO;
  737.                 break;
  738.  
  739.             default:
  740.                 print(HelpMsg);
  741.                 return (10);
  742.         }
  743.         argc--; argv++;
  744.     }
  745.  
  746.     numfiles  = argc - 1;
  747.     filenames = &argv[1];
  748.  
  749.     if (!sortfiles) {
  750.         /*
  751.          *        Just process files in the order they occur
  752.          */
  753.         for (i = 0; i < numfiles && !CtrlC; i++) {
  754.             if (unshar(filenames[i], NULL, numfiles > 1, overwrite) != 0)
  755.                 break;
  756.         }
  757.     } else {
  758.         /*
  759.          *        Do a prescan through all the files, and then unshar them
  760.          *        in the right order.
  761.          */    
  762.         typedef struct SortRecord {
  763.             struct  SortRecord *next;    /* Next record in list                */
  764.             int        index;                /* Index into filenames [] array    */
  765.             int        seqnum;                /* Sequence number of file            */
  766.             char    name[100];            /* Original subject line            */
  767.         } SORT;
  768.  
  769.         SORT *filehdrs, *head = NULL, *cur;
  770.  
  771.         filehdrs = AllocMem(sizeof(SORT) * numfiles, 0);
  772.         if (!filehdrs) {
  773.             print("Couldn't allocate memory to store file headers\n");
  774.             goto endsort;
  775.         }
  776.  
  777.         for (i = 0; i < numfiles && !CtrlC; i++) {
  778.             int seqnum;
  779.             SORT **ptr;
  780.  
  781.             if (!readheader(filenames[i], filehdrs[i].name, &seqnum))
  782.                 continue;    /* If couldn't read file, move to next file */
  783.  
  784.             /*
  785.              *        Now insert name at correct position in linked list
  786.              */
  787.             for (ptr = &head; *ptr && (*ptr)->seqnum <= seqnum;
  788.                                                 ptr = &(*ptr)->next)
  789.                 ;
  790.             filehdrs[i].next = *ptr;
  791.             *ptr = &filehdrs[i];
  792.  
  793.             filehdrs[i].seqnum = seqnum;
  794.             filehdrs[i].index  = i;
  795.         }
  796.         /*
  797.          *        Now we have a sorted list of files, so just walk down
  798.          *        the list unsharing files as we go.
  799.          */
  800.         for (cur = head; cur && !CtrlC; cur = cur->next) {
  801.             if (unshar(filenames[cur->index], cur->name,
  802.                                             numfiles > 1, overwrite) != 0)
  803.                 break;
  804.         }
  805. endsort:
  806.         if (filehdrs)
  807.             FreeMem(filehdrs, sizeof(SORT) * numfiles);
  808.     }
  809.  
  810.     /*
  811.      *        All files handled, now just tidy up and exit. If CtrlC was
  812.      *        pressed, let the user know.
  813.      */
  814.     if (CtrlC)
  815.         print("^C\n");
  816. }
  817.