home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Amiga / Workbench / Archivers / unsharWOS.lha / unsharppc / src / unshar.c.bak < prev    next >
Text File  |  1998-04-12  |  30KB  |  792 lines

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