home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 200-299 / ff287.lzh / UnShar / src / unshar.c < prev    next >
C/C++ Source or Header  |  1989-12-06  |  14KB  |  589 lines

  1. /*
  2.  *                     Unshar V1.1 (C) Copyright Eddy Carroll 1989
  3.  *                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4.  * Usage: Unshar {-o} <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.  * DISTRIBUTION
  36.  * I retain copyright rights to this source code, though it may be _freely_
  37.  * distributed. The executable file created from this source code is in
  38.  * the Public Domain and may be distributed without any restrictions.
  39.  *
  40.  */
  41.  
  42. /* Compiles under Lattice V5.04 */
  43.  
  44. #ifndef LATTICE_50 
  45. #include "system.h"
  46. #endif
  47.  
  48. #define YES            1
  49. #define NO            0
  50. #define CR            '\015'
  51. #define EOL            '\012'
  52. #define SINGLEQUOTE '\''
  53. #define DOUBLEQUOTE '\042'
  54. #define MAXSTRING    512        /* Maximum length of input line */
  55.  
  56. /*
  57.  *        New handler for Ctrl-C. Checks if CTRL-C received, and if it has,
  58.  *        sets the global CtrlC variable to true.
  59.  */
  60. #define chkabort() (CtrlC |= ((SetSignal(0,0) & SIGBREAKF_CTRL_C)))
  61.  
  62.  
  63. char HelpMsg[] = "\
  64. Unshar by Eddy Carroll 1989 Public Domain, extracts files from shar archives.\
  65. \n\
  66. Usage: unshar {-overwrite} <filename> ...\n";
  67.  
  68. char DiskMsg[]  = "Unshar aborted - Disk write error (disk full?)\n";
  69. char ErrorMsg[] = "Unshar: Invalid CAT or SED command at line ";
  70.  
  71. int linenum;
  72. int CtrlC = NO;
  73.  
  74. /*
  75.  * --------------------------------------------------------------------------
  76.  * The following block may be removed `as-is' and used in other programs.
  77.  * It provides basic buffered i/o on two files, an input file and an output
  78.  * file. It also provides output to the current standard output via
  79.  * print. Buffering is done using buffers of size MAXBUF.
  80.  *
  81.  * The following routines are provided:
  82.  *
  83.  * getc() returns an integer corresponding to the next character read from
  84.  * infile, or EOF if the end of file has been reached.
  85.  *
  86.  * putc(c) outputs a character to outfile. If a diskerror occurs, the global
  87.  * diskerror is set to YES, and all further diskwrites are ignored.
  88.  *
  89.  * getline() returns a pointer to a string containing the next line
  90.  * read in from infile. getline() also checks for CTRL-C via chkabort()
  91.  * 
  92.  * putline(s) outputs a string to outfile, returning non-zero if an
  93.  * error occurred during the write.
  94.  *
  95.  * flushin() resets getc() and getline() for input from a new file
  96.  *
  97.  * flushout() flushes output buffer; call prior to closing output file.
  98.  *
  99.  * input() returns a pointer to a string containing a line from stdin.
  100.  *
  101.  * print(s) prints a message on standard output.
  102.  *
  103.  * print3(s1,s2,s3) outputs three strings on standard output.
  104.  *
  105.  * numtostr(n) returns a pointer to the ascii representation of n.
  106.  *
  107.  * Special Notes
  108.  * ~~~~~~~~~~~~~
  109.  * You should ensure that you use the filenames 'infile' and 'outfile'
  110.  * when you are opening the input and output files in main(). Also,
  111.  * do not #define EOF or MAXBUF elsewhere in your program.
  112.  *
  113.  */
  114.  
  115. #define EOF        -1
  116. #define MAXBUF    10000
  117.  
  118. BPTR infile, outfile;
  119. LONG maxin = MAXBUF, maxout = MAXBUF, inbuf = MAXBUF, outbuf = 0;
  120. unsigned char inbuffer[MAXBUF], outbuffer[MAXBUF];
  121. int diskerror = NO;
  122.  
  123. /*
  124.  *        int getc()
  125.  *        ----------
  126.  *        Returns next character from infile, or EOF if end of file.
  127.  *
  128.  *        Replaced by a macro to improve performance. Original function was:
  129.  *
  130.  *        int getc()
  131.  *        {
  132.  *            if (!maxin)
  133.  *                return (EOF);
  134.  *
  135.  *            if (inbuf >= maxin) {
  136.  *                maxin = Read(infile, inbuffer, MAXBUF);
  137.  *                inbuf = 0;
  138.  *                if (!maxin)
  139.  *                    return (EOF);
  140.  *            }
  141.  *            return (inbuffer[inbuf++]);
  142.  *        }
  143.  *
  144.  */
  145. #define IF(x,y,z) ((x) ? (y) : (z))
  146.  
  147. #define getc()    IF(!maxin, EOF, \
  148.                 IF(inbuf >= maxin, ( \
  149.                     inbuf = 0, maxin = Read(infile, inbuffer, MAXBUF), \
  150.                     IF(!maxin, EOF, inbuffer[inbuf++]) \
  151.                 ), inbuffer[inbuf++])) \
  152.  
  153. /* 
  154.  *        Prepares getc() for input from a new file
  155.  */
  156. #define flushin() (maxin = MAXBUF, inbuf = MAXBUF)
  157.  
  158. /*
  159.  *        putc(ch)
  160.  *        --------
  161.  *        Outputs character ch to disk. If a diskerror is detected, then all
  162.  *        further output is ignored and the global diskerror is set to YES.
  163.  *
  164.  *        Replaced by a macro for performance reasons. Original function was:
  165.  *
  166.  *        void putc(ch)
  167.  *        int ch;
  168.  *        {
  169.  *            if (ch == EOF)
  170.  *                maxout = outbuf;
  171.  *            else
  172.  *                outbuffer[outbuf++] = ch;
  173.  *        
  174.  *            if (outbuf >= maxout) {
  175.  *                if (!diskerror && Write(outfile, outbuffer, maxout) == -1)
  176.  *                    diskerror = YES;
  177.  *                outbuf = 0;
  178.  *                maxout = MAXBUF;
  179.  *            }
  180.  *        }
  181.  */
  182. #define flushout() (maxout = outbuf, \
  183.                     IF(!diskerror && Write(outfile, outbuffer, maxout) == -1, \
  184.                         diskerror = YES, \
  185.                         0), \
  186.                     outbuf = 0, maxout = MAXBUF)
  187.  
  188. #define putc(ch) (outbuffer[outbuf++] = ch, \
  189.                   IF(outbuf >= maxout, \
  190.                       (IF (!diskerror && \
  191.                             Write(outfile, outbuffer, maxout) == -1, \
  192.                         diskerror = YES, \
  193.                         0), \
  194.                      outbuf = 0, maxout = MAXBUF), \
  195.                     0))
  196.  
  197. /*
  198.  *        print(s)
  199.  *        --------
  200.  *        Outputs a message to std output
  201.  */
  202. void print(s)
  203. char *s;
  204. {
  205.     Write(Output(),s,strlen(s));
  206. }
  207.  
  208. /*
  209.  *        print3()
  210.  *        --------
  211.  *        Outputs three strings to std output.
  212.  *        Useful for sequences like print3("string", variable, "string");
  213.  */
  214. void print3(s1,s2,s3)
  215. char *s1,*s2,*s3;
  216. {
  217.     print(s1);
  218.     print(s2);
  219.     print(s3);
  220. }
  221.  
  222. /*
  223.  *        getline()
  224.  *        ---------
  225.  *        Reads in a line from current infile into string, and returns a
  226.  *        pointer to that string. Returns NULL if EOF encountered.
  227.  */
  228. char *getline()
  229. {
  230.     register int ch, i = 0;
  231.     static char line[MAXSTRING];
  232.  
  233.     ch = getc();
  234.     if (ch == EOF)
  235.         return (NULL);
  236.  
  237.     while (i < (MAXSTRING-1) && ch != EOF && ch != EOL) {
  238.         line[i++] = ch;
  239.         ch = getc();
  240.     }
  241.  
  242.     line[i] = '\0';
  243.     linenum++;
  244.     chkabort();
  245.     return (line);
  246. }
  247.  
  248. /*
  249.  *        putline()
  250.  *        ---------
  251.  *        Outputs a string to the current output file (terminating it with LF).
  252.  *        Returns 0 for success, non-zero for disk error
  253.  */
  254. int putline(s)
  255. char *s;
  256. {
  257.     while (*s)
  258.         putc(*s++);
  259.     putc(EOL);
  260.     return (diskerror);
  261. }
  262.  
  263. /*
  264.  *        input()
  265.  *        -------
  266.  *        Reads a line from keyboard and returns pointer to it
  267.  */
  268. char *input()
  269. {
  270.     static char s[80];
  271.  
  272.     s[Read(Input(),s,75)] = '\0';
  273.     return(s);
  274. }
  275.  
  276. /*
  277.  *        numtostr()
  278.  *        ----------
  279.  *        Converts integer to string and returns pointer to it.
  280.  */
  281. char *numtostr(n)
  282. int n;
  283. {
  284.     static char s[20];
  285.     int i = 19;
  286.  
  287.     s[19] = '\0';
  288.     if (n)
  289.         while (n)
  290.             s[--i] = '0' + (n % 10), n /= 10;
  291.     else
  292.         s[--i] = '0';
  293.     return(&s[i]);
  294. }
  295.  
  296. /*
  297.  *        --------------------* End of Buffered IO routines *-----------------
  298.  */
  299.  
  300. /*
  301.  *        index()
  302.  *        -------
  303.  *        Like standard Unix index(), but skips over quotes if skip == true.
  304.  *        Also skips over chars prefixed by a \. Returns pointer to first
  305.  *        occurance of char c inside string s, or NULL.
  306.  */
  307. char *index(s,c,skip)
  308. char *s,c;
  309. int skip;
  310. {
  311.     register char *p = s;
  312.     register int noquotes = YES, literal = NO;
  313.  
  314.     while (*p) {
  315.         if (literal) {
  316.             literal = NO;
  317.             p++;
  318.         } else {
  319.             if (skip && ((*p == SINGLEQUOTE) || (*p == DOUBLEQUOTE)))
  320.                 noquotes = !noquotes;
  321.             if (noquotes && (*p == c))
  322.                 return(p);
  323.             literal = (*p == '\\');
  324.             p++;
  325.         }
  326.     }
  327.     return (NULL);
  328. }
  329.  
  330. /*
  331.  *        getname()
  332.  *        ---------
  333.  *        Extracts a string from start of string s1 and stores it in s2.
  334.  *        Leading spaces are discarded, and quotes, if present, are used to
  335.  *        indicate the start and end of the filename. If mode is MODE_FILE,
  336.  *        then if the name starts with either './' or '/', this prefix is
  337.  *        stripped. This doesn't happen if the mode is MODE_TEXT. A pointer
  338.  *        to the first character after the string in s1 is returned. In
  339.  *        addition, any characters prefixed with are passed through without
  340.  *        checking.
  341.  */
  342.  
  343. #define MODE_FILE 1
  344. #define MODE_TEXT 2
  345.  
  346. char *getname(s1,s2,mode)
  347. char *s1,*s2;
  348. {
  349.     char endchar = ' ';
  350.  
  351.     while (*s1 == ' ')
  352.         s1++;
  353.  
  354.     if (*s1 == SINGLEQUOTE || *s1 == DOUBLEQUOTE)
  355.         endchar = *s1++;
  356.  
  357.     if (mode == MODE_FILE) {
  358.         if (s1[0] == '.' && s1[1] == '/')
  359.             s1 += 2;
  360.         while (*s1 == '/')
  361.             s1++;
  362.     }
  363.  
  364.     while (*s1 && *s1 != endchar) {
  365.         if (*s1 == '\\' && *(s1+1))
  366.             s1++;
  367.         *s2++ = *s1++;
  368.     }
  369.     *s2 = '\0';
  370.  
  371.     if (*s1 == endchar)
  372.         return(++s1);
  373.     else
  374.         return(s1);
  375. }
  376.  
  377.  
  378. /*
  379.  *        checkfordir()
  380.  *        -------------
  381.  *        Checks filename to see if it is inside a subdirectory. If it is,
  382.  *        then checks if subdirectory exists, and creates it if it doesn't.
  383.  */
  384. void checkfordir(filename)
  385. char *filename;
  386. {
  387.     char dir[80], *p;
  388.     int i, x;
  389.     long dirlock, Lock(), CreateDir();
  390.  
  391.     p = filename;
  392.  
  393.     while (p = index(p, '/', 1)) {
  394.  
  395.         /* Dir exists, so copy dir part of filename into dir name area */
  396.  
  397.         x = p - filename;
  398.         for (i = 0; i < x; i++)
  399.             dir[i] = filename[i];
  400.         dir[i] = '\0';
  401.  
  402.         /* Now, see if directory exists, if not then create */
  403.         if ((dirlock = Lock(dir,ACCESS_READ)) == NULL) {
  404.             dirlock = CreateDir(dir);
  405.             if (dirlock) {
  406.                 print3("Creating directory ", dir, "\n");
  407.             }
  408.         }
  409.         if (dirlock)
  410.             UnLock(dirlock);
  411.  
  412.         p++;
  413.     }
  414. }
  415.  
  416. /*
  417.  *        unshar()
  418.  *        --------
  419.  *        Extracts all stored files from a shar file. Returns 0 for success,
  420.  *        non-zero if disk error occurred. If echofilename is non-zero, then
  421.  *        the name of each shar file is output before unsharing it. If
  422.  *        overwrite is non-zero, then existing files are overwritten without
  423.  *        any warning.
  424.  */
  425. int unshar(sharfile, echofilename, overwrite)
  426. char *sharfile;
  427. int echofilename, overwrite;
  428. {
  429.     register char *s, *p;
  430.     char endmarker[100], filename[100],sedstring[100];
  431.     int endlen, stripfirst, startfile, found = NO, err = NO, skip, sedlen;
  432.     long filelock, Lock();
  433.  
  434.     if ((infile = Open(sharfile, MODE_OLDFILE)) == NULL) {
  435.         print3("Can't open file ", sharfile, " for input\n");
  436.         return(0);
  437.     }
  438.  
  439.     linenum = 0;
  440.     if (echofilename)
  441.         print3("\033[7m Shar file: ", sharfile, " \033[0m\n");
  442.  
  443.     while (!err && !CtrlC && (s = getline()) != NULL) {
  444.         startfile = NO;
  445.         if (strncmp(s,"cat ",4) == 0) {
  446.             startfile  = YES;
  447.             stripfirst = NO;
  448.         }
  449.         if (strncmp(s,"sed ",4) == 0) {
  450.             startfile  = YES;
  451.             stripfirst = YES;
  452.             sedlen = 0;
  453.             /*
  454.              *        Note - tons of sanity checks done here to ensure that a
  455.              *        sed line of the form:
  456.              *
  457.              *            sed >s/somefile <<'endmarker' -e 's/X//'
  458.              *
  459.              *        Will be interpreted correctly.
  460.              */
  461.  
  462. #define ISPREFIX(ch)    (ch == DOUBLEQUOTE || ch == SINGLEQUOTE || ch == ' ')
  463. #define ISMETA(ch)        (ch == '<' || ch == '>')
  464. #define ISOK(s)            (s[1] == '/' && ISPREFIX(s[-1]) && !ISMETA(s[-2]))
  465.  
  466.             p = s;
  467.             while ((p = index(p,'s',0)) != NULL && !ISOK(p))
  468.                 p++;
  469.             if (p != NULL) {
  470.                 p += 2;                /* Skip over the 's/' bit    */
  471.                 if (*p == '^')        /* Skip past starting char    */
  472.                     p++;
  473.                 while (*p && *p != '/')
  474.                     sedstring[sedlen++] = *p++;
  475.             } 
  476.         }
  477.  
  478.         if (startfile) {    
  479.             if (found == NO) {
  480.                 found = YES;
  481.             }
  482.             if ((p = index(s,'>',1)) == NULL) {
  483.                 print3(ErrorMsg, numtostr(linenum), "(a)\n");
  484.             } else {
  485.                 /*
  486.                  *       This next bit is because I came across a weird shar
  487.                  *       script that created all its files using >> instead of >
  488.                  */
  489.                 if (*++p == '>')
  490.                     p++;
  491.                 getname(p,filename,MODE_FILE);
  492.                 p = s;
  493.                 while ((p = index(p,'<',1)) && (p[1] != '<'))
  494.                     ;
  495.                 if (p)
  496.                     getname(p+2,endmarker,MODE_TEXT);
  497.  
  498.                 endlen = strlen(endmarker);
  499.  
  500.                 if (strlen(filename) && endlen) {
  501.  
  502.                     checkfordir(filename);
  503.  
  504.                     /* Found a valid line so perform extract */
  505.  
  506.                     /* Check if file exists */
  507.  
  508.                     skip = NO;
  509.                     if (!overwrite) {
  510.                         filelock = Lock(filename, ACCESS_READ);
  511.                         if (filelock) {
  512.                             UnLock(filelock);
  513.                             print3("Overwrite file ", filename,
  514.                                 " (Yes, [No], All)? ");
  515.  
  516.                             switch (tolower(*input())) {
  517.  
  518.                             case 'a':
  519.                                 overwrite = YES;
  520.                                 break;
  521.                             case 'y':
  522.                                 skip = NO;
  523.                                 break;
  524.                             default:
  525.                                 skip = YES;
  526.                                 break;
  527.                             }
  528.                         }
  529.                     }
  530.  
  531.                     if ((outfile = Open(filename,MODE_NEWFILE)) == NULL)
  532.                         print3("Couldn't open file ",filename," for output\n");
  533.                     else {
  534.                         if (!skip)
  535.                             print3("Unsharing file ", filename, "\n");
  536.                         s = getline();
  537.                         err = NO;
  538.                         while (s && strncmp(s,endmarker,endlen) && !CtrlC) {
  539.                             if (stripfirst && !strncmp(sedstring,s,sedlen))
  540.                                 s += sedlen;
  541.                             if (!skip && (err = putline(s)))
  542.                                 break;
  543.                             s = getline();
  544.                         }
  545.                         flushout();
  546.                         if (err)
  547.                             print(DiskMsg);
  548.                         Close(outfile);
  549.                     }
  550.                 } else
  551.                     print(ErrorMsg, numtostr(linenum), "\n");
  552.             }
  553.         }
  554.     }
  555.     if (!err && !CtrlC)
  556.         if (found)
  557.             print("Unshar done\n");
  558.         else
  559.             print("No files to unshar\n");
  560.     Close(infile);
  561.     flushin();
  562.     return(err);
  563. }
  564.  
  565. /*
  566.  *        Start of mainline
  567.  */
  568. int main(argc,argv)
  569. int argc;
  570. char *argv[];
  571. {
  572.  
  573.     int i, ok, overwrite = NO;
  574.  
  575.     if ((argc == 1) || (*argv[1] == '?')) {
  576.         print(HelpMsg);
  577.         return (10);
  578.     }
  579.     for (i = 1, ok = YES; ok && i < argc && !CtrlC; i++) {
  580.         if (*argv[i] == '-' && (argv[i][1] == 'o' || argv[i][1] == 'O'))
  581.             overwrite = YES;
  582.         else
  583.             ok = !unshar(argv[i], argc > 2, overwrite);
  584.     }
  585.  
  586.     if (CtrlC)
  587.         print("^C\n");
  588. }
  589.