home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / shar349.zip / src / unshar.c < prev    next >
C/C++ Source or Header  |  1992-02-22  |  14KB  |  526 lines

  1. char *revision = "3.49";
  2. char RCS_ID[] = "$Header: /u/rhg/src/shar/unshar.c,v 3.49 90/09/12 15:15:17 rhg Exp $";
  3. /****************************************************************
  4.  * unshar.c: Unpackage one or more shell archive files
  5.  *
  6.  * Usage:     unshar [ -c ] [ -e | -E exit_line ] [ -d directory ] [ file ... ]
  7.  *
  8.  * Description:    unshar is a filter which removes the front part
  9.  *        of  a file and passes the rest to the 'sh' command.
  10.  *        It understands phrases like "cut here", and also
  11.  *        knows about shell comment characters and the Unix
  12.  *        commands "echo", "cat", and "sed".
  13.  *
  14.  *        The -c flag is passed through to the shell as a parameter to the script
  15.  *
  16.  *        It can unshar shar files concatenated in one file, with the
  17.  *        the "-e" command, which separates files by recognizing the
  18.  *        "exit 0" string at the beginning of a line
  19.  *
  20.  *        (The -E string option allows you to specify this string, thus
  21.  *        -e is equivalent to -E "exit 0")
  22.  *
  23.  *        The -d flag tells unshar to change directory before unsharing
  24.  *
  25.  * HISTORY
  26.  * 12-Sep-90  Richard H. Gumpertz (rhg@cps.com)
  27.  *        use fprintf instead of printf when printing error return from getwd.
  28.  *        deleted unused initialization of more_to_read in process.
  29.  *        changed ch from char to int in process so the EOF check would work.
  30.  *  4-Aug-90  Richard H. Gumpertz (rhg@cps.com)
  31.  *        renamed -c and -C to -e and -E and added -c flag (passed through to sh)
  32.  * 19-Apr-90  Colas Nahaboo (colas@mirsa.inria.fr)
  33.  *        added -c and -C flags to read from concatenated archives
  34.  *  1-Feb-85  Guido van Rossum (guido@mcvax) at CWI, Amsterdam
  35.  *        Added missing 'quit' routine;
  36.  *        added -d flag to change to directory first;
  37.  *        added filter mode (read stdin when no arguments);
  38.  *        added 'getopt' to get flags (makes it self-contained).
  39.  * 29-Jan-85  Michael Mauldin (mlm) at Carnegie-Mellon University
  40.  *        Created.
  41.  ****************************************************************/
  42. /*+:EDITS:*/
  43. /*:08-04-1990-15:54-rhg@cps.com-changes listed above (-c/-C => -e/-E, new -c) */
  44. /*:05-05-1990-01:37-relay.EU.net!rivm!a3-dont assume vax is running BSD */
  45. /*:04-19-1990-15:20-wht@n4hgf-fix so -d doesnt make argv files unreachable */
  46. /*:04-19-1990-15:06-wht@n4hgf-colas@mirsa patches had expanded tabs */
  47. /*:04-10-1990-22:02-wht@n4hgf-stdin failed sometimes-can not seek on pipe */
  48.  
  49. /* MS-DOS port (c) 1990 by Thorsten Ohl, ohl@gnu.ai.mit.edu
  50.    This port is distributed under the terms of the
  51.    GNU General Public License as published by the
  52.    Free Software Foundation.  */
  53.  
  54. #include <stdio.h>
  55. #define EOL '\n'
  56.  
  57. #ifdef MSDOS
  58.  
  59. #define VOID void
  60.  
  61. #include <stdlib.h>
  62. #include <string.h>
  63. #include <direct.h>
  64. #include <errno.h>
  65. #include <process.h>
  66. #include <io.h>
  67.  
  68. #ifdef USE_GNU_GETOPT
  69. #include <getopt.h>
  70. #endif
  71.  
  72. #include <gnulib.h>
  73. char *program_name;
  74.  
  75. /* We make this program selfcontained by providing our own pipes.
  76.    This also avoids the neccessity of invoking a subshell (which
  77.    may turn out to be a command.com ....  */
  78.  
  79. #define pipe_file (_pipe_file (0))
  80. extern char *_pipe_file (int n);
  81. extern int filter_through_command (char *infile, char *outfile,
  82.                    char *command, ...);
  83.  
  84. extern void main (int argc, char **argv);
  85. static void process (char *name, FILE * in);
  86. static int position (char *fn, FILE * fil, long start);
  87. static int stlmatch (char *big, char *small);
  88. static int smatch (char *dat, char *pat, char **res);
  89. static void quit (int status, char *message);
  90.  
  91. #define USE_GETCWD
  92. #define getcwd(buf, len)  msdos_format_filename (getcwd (buf, len))
  93.  
  94. #else /* not MSDOS */
  95.  
  96. #define VOID
  97.  
  98. char *strchr();
  99. #if (defined(pyr) || defined(sun) || defined(BSD42) || \
  100.  defined(vax) || defined(sequent)) && !defined(SYS5)
  101. #define strchr    index
  102. #undef USE_GETCWD
  103. char *getwd();
  104. #else
  105. #define USE_GETCWD
  106. char *getcwd();
  107. #endif
  108.  
  109. #endif /* not MSDOS */
  110.  
  111. extern char *optarg;
  112. extern int optind;
  113.  
  114. int c_flag = 0;
  115. int continue_reading = 0;
  116. char *exit_string = "exit 0";
  117. int exit_string_length;
  118. char argvdir[1024];
  119.  
  120. VOID
  121. main(argc,argv)
  122. int argc;
  123. char *argv[];
  124. {
  125.     int i,ch;
  126.     FILE *in;
  127.     char s1024[1024];
  128.  
  129. #ifdef MSDOS
  130.     program_name = argv[0];
  131. #endif
  132.  
  133.     setbuf(stdout,NULL);
  134.     setbuf(stderr,NULL);
  135.  
  136. #ifdef USE_GETCWD
  137.     if(!getcwd(argvdir,sizeof(argvdir)))
  138.     {
  139.         perror("cannot get current directory name");
  140.         exit(1);
  141.     }
  142. #else
  143.     argvdir[0] = 0;
  144.     if(!getwd(argvdir))
  145.     {
  146.         if(argvdir[0])
  147.             fprintf(stderr,"%s\n",argvdir);
  148.         else
  149.             fprintf(stderr,"cannot get current directory name\n");
  150.         exit(1);
  151.     }
  152. #endif
  153.  
  154.  
  155.     /* Process options */
  156.  
  157.     while((ch = getopt(argc,argv,"cd:eE:")) != EOF)
  158.     {
  159.         switch(ch)
  160.         {
  161.         case 'c':
  162.             c_flag = 1;
  163.             break;
  164.         case 'd':
  165.             if(chdir(optarg) == -1)
  166.             {
  167.                 fprintf(stderr,"unshar: cannot chdir to '%s'\n",optarg);
  168.                 exit(2);
  169.             }
  170.             break;
  171.         case 'E':
  172.             exit_string = optarg;
  173.         case 'e':
  174.             continue_reading = 1;
  175.             exit_string_length = strlen(exit_string);
  176.             break;
  177.         default:
  178.             quit(2,"Usage: unshar [-c] [-e | -E exit_line] [-d directory] [file ...]\n");
  179.         }
  180.     }
  181.  
  182.     if(optind < argc)
  183.     {
  184.         for(i= optind; i < argc; ++i)
  185.         {
  186.             if(argv[i][0] == '/') {
  187.                 strcpy(s1024,argv[i]);
  188.             } else {
  189.                 strcpy(s1024,argvdir);
  190.                 strcat(s1024,"/");
  191.                 strcat(s1024,argv[i]);
  192.             }
  193.             if(!(in = fopen(s1024,"r")))
  194.             {
  195.                 perror(s1024);
  196.                 exit(1);
  197.             }
  198.             process(s1024,in);
  199.             fclose(in);
  200.         }
  201.     }
  202.     else
  203.     {
  204.         sprintf(s1024,"/tmp/unsh.%05d",getpid());
  205.         unlink(s1024);
  206.         if(!(in = fopen(s1024,"w+")))
  207.         {
  208.             fprintf(stderr,"cannot open temp file '%s'\n",s1024);
  209.             exit(1);
  210.         }
  211.         unlink(s1024);    /* don't try this with MSDOS, sports fans */
  212.         while(i = fread(s1024,1,sizeof(s1024),stdin))
  213.             fwrite(s1024,i,1,in);
  214.         rewind(in);
  215.         process("standard input",in);
  216.         fclose(in);
  217.     }
  218.  
  219.     exit(0);
  220. }
  221.  
  222.  
  223. VOID
  224. process(name,in)
  225. char *name;
  226. FILE *in;
  227. {
  228.     char buffer[8196];
  229.     int ch;
  230.     FILE *shpr,*popen();
  231.     long current_position = 0;
  232.     char *more_to_read;
  233.  
  234.     while(position(name,in,current_position))
  235.     {
  236.         printf("%s:\n",name);
  237. #ifdef MSDOS
  238.         if ((shpr = fopen (pipe_file, "w")) == NULL)
  239.           error (1, 0, "pipe error: %s", pipe_file);
  240. #else
  241.         if(!(shpr = popen((c_flag ? "sh -s -c" : "sh"),"w")))
  242.             quit(1,"unshar: cannot open 'sh' process\n");
  243. #endif
  244.         if (!continue_reading) {
  245.             while((ch = fgetc(in)) != EOF)
  246.                 fputc(ch,shpr);
  247.  
  248. #ifdef MSDOS
  249.             fclose (shpr);
  250.             if (filter_through_command (pipe_file, NULL, "sh",
  251.                             c_flag ? "-s" : NULL,
  252.                             "-c", NULL))
  253.               error (1, 0, "sh failed");
  254. #else
  255.             pclose(shpr);
  256. #endif
  257.             break;
  258.         } else {
  259.             while (more_to_read = fgets(buffer, 8196, in)) {
  260.                 fputs(buffer, shpr);
  261.                 if (!strncmp(exit_string, buffer, exit_string_length)) {
  262.                     break;
  263.                 }
  264.             }
  265. #ifdef MSDOS
  266.             fclose (shpr);
  267.             if (filter_through_command (pipe_file, NULL, "sh",
  268.                             c_flag ? "-s" : NULL,
  269.                             "-c", NULL))
  270.               error (1, 0, "sh failed");
  271. #else
  272.             pclose(shpr);
  273. #endif
  274.             if (more_to_read)
  275.                 current_position = ftell(in);
  276.             else {
  277.                 break;
  278.             }
  279.         }
  280.     }
  281. }
  282.  
  283. /****************************************************************
  284.  * position: position 'fil' at the start of the shell command
  285.  * portion of a shell archive file.
  286.  ****************************************************************/
  287.  
  288. position(fn,fil,start)
  289. char *fn;
  290. FILE *fil;
  291. long start;                   /* scan file from position */
  292. {
  293.     char buf[BUFSIZ];
  294.     long pos,ftell();
  295.  
  296.     /* Results from star matcher */
  297.     static char res1[BUFSIZ],res2[BUFSIZ],res3[BUFSIZ],res4[BUFSIZ];
  298.     static char *result[] = 
  299.     {
  300.         res1,res2,res3,res4         };
  301.  
  302.     fseek(fil, start, 0);
  303.  
  304.     while(1)
  305.     { /* Record position of the start of this line */
  306.         pos = ftell(fil);
  307.  
  308.         /* Read next line, fail if no more and no previous process */
  309.         if(!fgets(buf,BUFSIZ,fil))
  310.         {
  311.             if(!start)
  312.                 fprintf(stderr,"unshar: found no shell commands in %s\n",fn);
  313.             return(0);
  314.         }
  315.  
  316.         /* Bail out if we see C preprocessor commands or C comments */
  317.         if(stlmatch(buf,"#include")    || stlmatch(buf,"# include") ||
  318.             stlmatch(buf,"#define")    || stlmatch(buf,"# define") ||
  319.             stlmatch(buf,"#ifdef")    || stlmatch(buf,"# ifdef") ||
  320.             stlmatch(buf,"#ifndef")    || stlmatch(buf,"# ifndef") ||
  321.             stlmatch(buf,"/*"))
  322.         {
  323.             fprintf(stderr,
  324.                 "unshar: %s looks like raw C code, not a shell archive\n",fn);
  325.             return(0);
  326.         }
  327.  
  328.         /* Does this line start with a shell command or comment */
  329.         if(stlmatch(buf,"#")    || stlmatch(buf,":") ||
  330.             stlmatch(buf,"echo ")    || stlmatch(buf,"sed ") ||
  331.             stlmatch(buf,"cat ") || stlmatch(buf,"if "))
  332.         {
  333.             fseek(fil,pos,0);
  334.             return(1);
  335.         }
  336.  
  337.         /* Does this line say "Cut here" */
  338.         if(smatch(buf,"*CUT*HERE*",result) ||
  339.             smatch(buf,"*cut*here*",result) ||
  340.             smatch(buf,"*TEAR*HERE*",result) ||
  341.             smatch(buf,"*tear*here*",result) ||
  342.             smatch(buf,"*CUT*CUT*",result) ||
  343.             smatch(buf,"*cut*cut*",result))
  344.         {
  345.             /* Read next line after "cut here", skipping blank lines */
  346.             while(1)
  347.             {
  348.                 pos = ftell(fil);
  349.  
  350.                 if(!fgets(buf,BUFSIZ,fil))
  351.                 {
  352.                     fprintf(stderr,
  353.                         "unshar: found no shell commands after 'cut' in %s\n",fn);
  354.                     return(0);
  355.                 }
  356.  
  357.                 if(*buf != '\n') break;
  358.             }
  359.  
  360.             /* Win if line starts with a comment character of lower case letter */
  361.             if(*buf == '#' || *buf == ':' || (('a' <= *buf) && ('z' >= *buf)))
  362.             {
  363.                 fseek(fil,pos,0);
  364.                 return(1);
  365.             }
  366.  
  367.             /* Cut here message lied to us */
  368.             fprintf(stderr,"unshar: %s is probably not a shell archive,\n",fn);
  369.             fprintf(stderr,"        the 'cut' line was followed by: %s",buf);
  370.             return(0);
  371.         }
  372.     }
  373. }
  374.  
  375. /*****************************************************************
  376.  * stlmatch  --  match leftmost part of string
  377.  *
  378.  * Usage:  i = stlmatch (big,small)
  379.  *    int i;
  380.  *    char *small, *big;
  381.  *
  382.  * Returns 1 iff initial characters of big match small exactly;
  383.  * else 0.
  384.  *
  385.  * HISTORY
  386.  * 18-May-82 Michael Mauldin (mlm) at Carnegie-Mellon University
  387.  *      Ripped out of CMU lib for Rog-O-Matic portability
  388.  * 20-Nov-79  Steven Shafer (sas) at Carnegie-Mellon University
  389.  *    Rewritten for VAX from Ken Greer's routine.
  390.  *
  391.  *  Originally from klg (Ken Greer) on IUS/SUS UNIX
  392.  *****************************************************************/
  393.  
  394. int stlmatch(big,small)
  395. char *small,*big;
  396. {
  397.     register char *s,*b;
  398.     s = small;
  399.     b = big;
  400.     do
  401.     {
  402.         if(*s == '\0')
  403.             return(1);
  404.     }  while(*s++ == *b++);
  405.     return(0);
  406. }
  407.  
  408. /*****************************************************************
  409.  * smatch: Given a data string and a pattern containing one or
  410.  * more embedded stars (*) (which match any number of characters)
  411.  * return true if the match succeeds, and set res[i] to the
  412.  * characters matched by the 'i'th *.
  413.  *****************************************************************/
  414.  
  415. smatch(dat,pat,res)
  416. register char *dat,*pat,**res;
  417. {
  418.     register char *star = 0,*starend,*resp;
  419.     int nres = 0;
  420.  
  421.     while(1)
  422.     {
  423.         if(*pat == '*')
  424.         {
  425.             star = ++pat;                  /* Pattern after * */
  426.             starend = dat;                  /* Data after * match */
  427.             resp = res[nres++];              /* Result string */
  428.             *resp = '\0';                  /* Initially null */
  429.         }
  430.         else if(*dat == *pat)              /* Characters match */
  431.         {
  432.             if(*pat == '\0')              /* Pattern matches */
  433.                 return(1);
  434.             pat++;                      /* Try next position */
  435.             dat++;
  436.         }
  437.         else
  438.         {
  439.             if(*dat == '\0')              /* Pattern fails - no more */
  440.                 return(0);                  /* data */
  441.             if(star == 0)                  /* Pattern fails - no * to */
  442.                 return(0);                  /* adjust */
  443.             pat = star;                  /* Restart pattern after * */
  444.             *resp++ = *starend;              /* Copy character to result */
  445.             *resp = '\0';                  /* null terminate */
  446.             dat = ++starend;                  /* Rescan after copied char */
  447.         }
  448.     }
  449. }
  450.  
  451. /*****************************************************************
  452.  * Addendum: quit subroutine (print a message and exit)
  453.  *****************************************************************/
  454.  
  455. VOID
  456. quit(status,message)
  457. int status;
  458. char *message;
  459. {
  460.     fprintf(stderr,message);
  461.     exit(status);
  462. }
  463.  
  464. #ifndef USE_GNU_GETOPT
  465. /*****************************************************************
  466.  * Public Domain getopt routine
  467.  *****************************************************************/
  468.  
  469. /*
  470.  * get option letter from argument vector
  471.  */
  472. int opterr = 1;        /* useless, never set or used */
  473. int optind = 1;        /* index into parent argv vector */
  474. int optopt;            /* character checked for validity */
  475. char *optarg;        /* argument associated with option */
  476.  
  477. #define BADCH    (int)'?'
  478. #define EMSG    ""
  479. #define tell(s)    fputs(*nargv,stderr);fputs(s,stderr); \
  480.         fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);
  481.  
  482. getopt(nargc,nargv,ostr)
  483. int nargc;
  484. char **nargv,*ostr;
  485. {
  486.     static char *place = EMSG;    /* option letter processing */
  487.     register char *oli;        /* option letter list index */
  488.     char *strchr();
  489.  
  490.     if(!*place)
  491.     {            /* update scanning pointer */
  492.         if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place)
  493.             return(EOF);
  494.         if(*place == '-')
  495.         {    /* found "--" */
  496.             ++optind;
  497.             return(EOF);
  498.         }
  499.     }                /* option letter okay? */
  500.     if((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr,optopt)))
  501.     {
  502.         if(!*place) ++optind;
  503.         tell(": illegal option -- ");
  504.     }
  505.     if(*++oli != ':')
  506.     {        /* don't need argument */
  507.         optarg = (char *)0;
  508.         if(!*place) ++optind;
  509.     }
  510.     else 
  511.     {                /* need an argument */
  512.         if(*place) optarg = place;    /* no white space */
  513.         else if(nargc <= ++optind)
  514.         {    /* no arg */
  515.             place = EMSG;
  516.             tell(": option requires an argument -- ");
  517.         }
  518.         else optarg = nargv[optind];    /* white space */
  519.         place = EMSG;
  520.         ++optind;
  521.     }
  522.     return(optopt);            /* dump back option letter */
  523. }
  524. /* vi: set tabstop=4 shiftwidth=4: */
  525. #endif /* USE_GNU_GETOPT */
  526.