home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / NEWSHAR.ZIP / SHAR.C < prev    next >
C/C++ Source or Header  |  1992-02-02  |  13KB  |  592 lines

  1. /*------------------------------------------------------------------------------
  2.  
  3.     The UNIX program 'shar' (from "shell archive") puts readable text files
  4.     together in a package from which they are easy to extract by feeding the
  5.     package to 'sh'.
  6.  
  7.     Like its predecessors, this OS/2 implementation of shar builds shell
  8.     archives but is also capable of decomposing them in the absence of the
  9.     Bourne shell.  It is thus a useful tool for dealing with shell archives
  10.     created on UNIX systems.
  11.  
  12.     v 920202    J. Saxton
  13.     -    Recently I've seen some shell archives which are like no others I've
  14.         encountered previously.  These are characterised by having the shar
  15.         prefix only on those lines which begin with a space or tab.  Whereas
  16.         the number of such files has been so small as to be easy to deal with
  17.         manually, however the recent distribution of dmake version 3.8 in
  18.         this format has prompted an effort to accomodate it.
  19.  
  20.     v 920128    J. Saxton
  21.     -    During decomposition, allowed for concatenation to an existing file.
  22.  
  23.         e.g. the shar file might contain a line such as
  24.                     sed 's/^X//' << 'SHAR_EOF' > 'readme'
  25.         followed later by
  26.                     sed 's/^X//' << 'SHAR_EOF' >> 'readme'
  27.  
  28.     v 911225    J. Saxton
  29.     -    Corrected a problem in mkdir when directory name was NOT quoted.
  30.     -    Tidied up reporting of directory operations.
  31.  
  32.     v 911205    J. Saxton
  33.     -    This version strictly for OS/2 HPFS.  No longer is any attempt made
  34.         to rectify file names for FAT or CP/M file systems.  Code for VMS
  35.         has been deleted.  Code for ULTRIX and the Amiga has been retained
  36.         without verification.
  37.     -    Detect directories and don't try to add them to archives.
  38.         (Maybe next version will allow recursion into directories?)
  39.     -    Added dequote() function to normalize directory names.
  40.     -    Generated a Makefile and a definitions file.
  41.     -    made -pX the default pattern instead of -pXX.
  42.     -    fixed 'unshar' operation to ignore leading blanks.
  43.     -    tidied up source so it now compiles with MSC 6.0A at -W3 without
  44.         complaint.
  45.  
  46.     v 910525    J. Saxton for OS/2 2.0 and HPFS 
  47.  
  48.     v 891212    J. Saxton for MSDOS and OS/2
  49.     -    index() function changed to strchr() to comply with more modern C
  50.         compilers. 
  51.     -    eliminated several warning messages generated by the Microsoft C
  52.         compiler (version 5.1) 
  53.     -    compiled as a non-PM application for OS/2 and bound as a family mode
  54.         program for MSDOS.  Does wildcard expansion on input file names. 
  55.  
  56.             cl /Ox /Fb shar.c c:\os2\lib\setargv.obj
  57.  
  58.     v 860716    M. Kersenbrock (tektronix!copper!michaelk) for Z80-CPM
  59.     -    enhanced usage message 
  60.  
  61.     v 860712    D. Wecker for ULTRIX and the AMIGA
  62.     -    stripped down..does patterns but no directories
  63.     -    added a -u (unshar) switch 
  64.  
  65. ------------------------------------------------------------------------------*/
  66.  
  67. #ifdef MSDOS
  68. #define OS2
  69. #endif
  70.  
  71. #ifdef OS2
  72. #include <string.h>
  73. #include <ctype.h>
  74. #include <malloc.h>
  75. #include <stdlib.h>
  76. #include <direct.h>
  77. #include <sys/types.h>
  78. #include <sys/stat.h>
  79. #endif
  80.  
  81. #include <stdio.h>
  82. #include <io.h>
  83. #include <time.h>
  84. #include <errno.h>
  85.  
  86. #ifdef    AMIGA
  87. #include <exec/types.h>
  88. extern char *getenv(), *scdir(), *malloc(), *strchr();
  89. #endif
  90.  
  91. #ifdef    ULTRIX
  92. #include <sys/types.h>
  93. #include <sys/stat.h>
  94. extern char *getenv(), *scdir(), *malloc(), *strchr();
  95. #endif
  96.  
  97. int
  98.     getopt(int, char **, char *),
  99.     getpat(char *),
  100.     getarg(int, char **);
  101. void
  102.     dounshar(char *),
  103.     getshpar(char *, char *, char *),
  104.     dequote(char *);
  105.  
  106. #define BADCH    ((int)'?')
  107. #define EMSG    ""
  108. #define tell(s)    {fputs(*nargv,stderr);fputs((s),stderr);fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);}
  109. #define rescanopts()    (optind = 1)
  110.  
  111. int optind = 1,                    /* index into parent argv vector */
  112.     optopt;                        /* character checked for validity */
  113. long fsize;                        /* length of file */
  114. char *optarg;                    /* argument associated with option */
  115. char *sav[100];                    /* saved file names */
  116. int savind;                        /* save index */
  117.  
  118. /* OPTIONS */
  119. int Verbose = 0;                /* provide append/extract feedback */
  120. int Basename = 0;                /* extract into basenames */
  121. int Count = 0;                    /* count characters to check transfer */
  122. char *Delim = "SHAR_EOF";        /* put after each file */
  123. char Filter[100] = "cat";        /* used to extract archived files */
  124. char *Prefix = NULL;            /* line prefix to avoid funny chars */
  125. int UnShar = 0;                    /* do we unshar an input file? */
  126. int Concat = 0;                    /* unshar to end of existing file? */
  127.  
  128. char Usage1[] =
  129. "\nSHAR: Create/extract file archive for extraction by /bin/sh (normally).\n\
  130. \n\
  131. usage: shar [-u archive] [[-a] [-p prefix]\
  132.  [-d delim] [-bcv] files > archive]\n\
  133. \n\
  134.     where:    -a  all the options (v,c,b,-pX)\n";
  135.  
  136. char Usage2[] =
  137. "        -b  extract absolute paths into current directory\n\
  138.         -c  check filesizes on extraction\n\
  139.         -d  use this EOF delimiter instead of SHAR_EOF\n";
  140.  
  141. char Usage3[] =
  142. "        -p  use this as prefix to each line in archived files\n\
  143.         -u  unshar <archive>\n\
  144.         -v  verbose on extraction, incl. echoing filesizes\n";
  145.  
  146. struct stat
  147.     FileInfo;
  148.  
  149. #define    SED "sed 's/^%s//'"
  150.  
  151. #define    OPTSTRING "u:ap:d:bcv"
  152.  
  153. int header(char **ppchFiles)
  154. {
  155.     auto long clock;
  156.     char **ppchList;
  157.     char *pchOrg;
  158.     char *pchName;
  159.     register int problems = 0;
  160.  
  161.     pchOrg = getenv("ORGANIZATION");
  162.     pchName = getenv("NAME");
  163.  
  164.     puts("#\tThis is a shell archive.");
  165.     puts("#\tRemove everything above and including the cut line.");
  166.     puts("#\tThen run the rest of the file through sh.");
  167.     puts("#----cut here-----cut here-----cut here-----cut here----#");
  168.     puts("#!/bin/sh");
  169.     puts("# shar:    Shell Archiver");
  170.     puts("#\tRun the following text with /bin/sh to create:");
  171.     for (ppchList = ppchFiles; *ppchList; ++ppchList)
  172.         printf("#\t%s\n", *ppchList);
  173.  
  174.     (void) time(&clock);
  175.     printf("# This archive created: %s", ctime(&clock));
  176.  
  177.     if (pchName != NULL)
  178.         printf("# By:\t%s (%s)\n", pchName,
  179.                pchOrg == NULL ? "Dave Wecker Midnight Hacks" : pchOrg);
  180.     return (0);
  181. }
  182.  
  183. int archive(char *input, char *output)
  184. {
  185.     auto char line[BUFSIZ];
  186.     register FILE *ioptr;
  187.  
  188.     if (ioptr = fopen(input, "r"))
  189.     {
  190.         printf("%s << \\%s > %s\n", Filter, Delim, output);
  191.         while (fgets(line, BUFSIZ, ioptr))
  192.         {
  193.             if (Prefix != NULL)
  194.                 fputs(Prefix, stdout);
  195.             fputs(line, stdout);
  196.             if (Count)
  197.                 fsize += strlen(line);
  198.         }
  199.         puts(Delim);
  200.         (void) fclose(ioptr);
  201.         return (0);
  202.     }
  203.     else
  204.     {
  205.         fprintf(stderr, "shar: Can't open '%s'\n", input);
  206.         return (1);
  207.     }
  208. }
  209.  
  210. void shar(char *file)
  211. {
  212.     char *basefile;
  213.  
  214.     if (!strcmp(file, "."))
  215.         return;
  216.     fsize = 0;
  217.     if (stat(file, &FileInfo))
  218.     {
  219.         fprintf(stderr,"Unable to do a stat() on '%s'\n", file);
  220.         return;
  221.     }
  222.     if (FileInfo.st_mode & S_IFMT != S_IFREG)
  223.     {
  224.         basefile = "strange file";
  225.         if (FileInfo.st_mode & S_IFCHR)
  226.             basefile = "character special file";
  227.         if (FileInfo.st_mode & S_IFDIR)
  228.             basefile = "directory";
  229.         fprintf(stderr,"Cannot archive %s '%s'\n", basefile, file);
  230.         return;
  231.     }
  232.  
  233.     basefile = file;
  234.     if (Basename)
  235.     {
  236.         while (*basefile)
  237.             basefile++;            /* go to end of name */
  238.         while (basefile > file && *(basefile - 1) != '/')
  239.             basefile--;
  240.     }
  241.     if (Verbose)
  242.         printf("echo shar: extracting %s\n", basefile);
  243.     if (archive(file, basefile))
  244.         exit(66);
  245.     if (Count)
  246.     {
  247.         printf("if test %ld -ne \"`wc -c %s`\"\n", fsize, basefile);
  248.         printf("then\necho shar: error transmitting %s ", basefile);
  249.         printf("'(should have been %ld characters)'\nfi\n", fsize);
  250.     }
  251. }
  252.  
  253. void main(int argc, char *argv[])
  254. {
  255.     auto char *ppchFiles[256];
  256.     register int C;
  257.     register char **ppchList = ppchFiles;
  258.     register int errflg = 0;
  259.  
  260.     while (EOF != (C = getopt(argc, argv, OPTSTRING)))
  261.     {
  262.         switch (C)
  263.         {
  264.         case 'v':
  265.             Verbose++;
  266.             break;
  267.         case 'c':
  268.             Count++;
  269.             break;
  270.         case 'b':
  271.             Basename++;
  272.             break;
  273.         case 'd':
  274.             Delim = optarg;
  275.             break;
  276.         case 'a':                /* all the options */
  277.             optarg = "X";
  278.             Verbose++;
  279.             Count++;
  280.             Basename++;
  281.             /* fall through to set prefix */
  282.         case 'p':
  283.             (void) sprintf(Filter, SED, Prefix = optarg);
  284.             break;
  285.         case 'u':
  286.             UnShar++;
  287.             dounshar(optarg);
  288.             break;
  289.         default:
  290.             errflg++;
  291.         }
  292.     }
  293.     if (UnShar)
  294.         exit(0);
  295.  
  296.     C = getarg(argc, argv);
  297.     if (errflg || EOF == C)
  298.     {
  299.         if (EOF == C)
  300.             fprintf(stderr, "shar: No input files\n");
  301.         fprintf(stderr, "%s%s%s", Usage1, Usage2, Usage3);
  302.         exit(1);
  303.     }
  304.  
  305.     savind = 0;
  306.     do
  307.     {
  308.         if (getpat(optarg))
  309.             exit(2);
  310.     }
  311.     while (EOF != (C = getarg(argc, argv)));
  312.  
  313.     sav[savind] = NULL;
  314.     header(sav);
  315.     for (ppchList = sav; *ppchList; ++ppchList)
  316.     {
  317.         shar(*ppchList);
  318.     }
  319.     puts("#\tEnd of shell archive");
  320.     puts("exit 0");
  321.     exit(0);
  322. }
  323.  
  324. int getpat(char *pattern)
  325. {
  326.     register char *ptr;
  327. #ifdef AMIGA
  328.     while (ptr = scdir(pattern))
  329. #else
  330.     ptr = pattern;
  331. #endif
  332.     {
  333.         sav[savind] = malloc(strlen(ptr) + 1);
  334.         strcpy(sav[savind++], ptr);
  335.  
  336.         if (access(ptr, 4))
  337.         {
  338.             printf("No read access for file: %s\n", ptr);
  339.             return (-1);
  340.         }
  341.     }
  342.     return (0);
  343. }
  344.  
  345.  
  346. /*
  347.    get option letter from argument vector 
  348. */
  349.  
  350. int getopt(int nargc, char **nargv, char *ostr)
  351. {
  352.     register char *oli;            /* option letter list index */
  353.     static char *place = EMSG;    /* option letter processing */
  354.  
  355.     if (!*place)
  356.     {                            /* update scanning pointer */
  357.         if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place)
  358.             return (EOF);
  359.         if (*place == '-')
  360.         {                        /* found "--" */
  361.             ++optind;
  362.             return EOF;
  363.         }
  364.     }                            /* option letter okay? */
  365.     if ((optopt = (int) *place++) == (int) ':' || !(oli = strchr(ostr, optopt)))
  366.     {
  367.         if (!*place)
  368.             ++optind;
  369.         tell(": illegal option -- ");
  370.     }
  371.     if (*++oli != ':')
  372.     {                            /* don't need argument */
  373.         optarg = NULL;
  374.         if (!*place)
  375.             ++optind;
  376.     }
  377.     else
  378.     {                            /* need an argument */
  379.         if (*place)
  380.         {                        /* no white space */
  381.             optarg = place;
  382.         }
  383.         else
  384.         {
  385.             if (nargc <= ++optind)
  386.             {                    /* no arg */
  387.                 place = EMSG;
  388.                 tell(": option requires an argument -- ");
  389.             }
  390.             else
  391.             {
  392.                 optarg = nargv[optind];    /* white space */
  393.             }
  394.         }
  395.         place = EMSG;
  396.         ++optind;
  397.     }
  398.     return (optopt);            /* dump back option letter */
  399. }
  400.  
  401.  
  402. int getarg(int nargc, char **nargv)
  403. {
  404.     if (nargc <= optind)
  405.     {
  406.         optarg = (char *) 0;
  407.         return EOF;
  408.     }
  409.     else
  410.     {
  411.         optarg = nargv[optind++];
  412.         return 0;
  413.     }
  414. }
  415.  
  416. void dounshar(char *ArcNam)
  417. {
  418.     int
  419.         Prefix = 0,
  420.         i;
  421.     FILE
  422.         *inptr, *outptr;
  423.     char
  424.         *ptr,
  425.         *nlf,
  426.         line[BUFSIZ],
  427.         FilNam[256],
  428.         Delim[40],
  429.         PrefixPat[10];
  430.  
  431.     if (!(inptr = fopen(ArcNam, "r")))
  432.     {
  433.         fprintf(stderr, "shar: Can't open archive '%s'\n", ArcNam);
  434.         return;
  435.     }
  436.     while (fgets(line, BUFSIZ, inptr))
  437.     {
  438.         ptr = line + strspn(line, " \t");
  439.         if (strncmp(ptr, "sed ", 4) == 0)
  440.         {
  441.             memset(PrefixPat, 0, sizeof PrefixPat);
  442.             Prefix = 0;
  443.             if (!(ptr = strchr(ptr, '/')))
  444.                 goto getfil;
  445.             if (*++ptr == '^')
  446.                 ++ptr;
  447.             while (*ptr != '/')
  448.             {
  449.                 if (Prefix < sizeof PrefixPat - 2)
  450.                     PrefixPat[Prefix] = *ptr;
  451.                 ++Prefix;
  452.                 ++ptr;
  453.             }
  454.             ++ptr;
  455.             goto getfil;
  456.         }
  457.         else
  458.         if (strncmp(ptr, "cat ", 4) == 0)
  459.         {
  460.             Prefix = 0;
  461.     getfil:
  462.             getshpar(ptr, ">", FilNam);
  463.             getshpar(ptr, "<<", Delim);
  464.             if (Concat)
  465.             {
  466.                 fprintf(stderr, "Appending to %s ...", FilNam);
  467.                 outptr = fopen(FilNam, "a");
  468.             }
  469.             else
  470.             {
  471.                 fprintf(stderr, "Creating %s ...", FilNam);
  472.                 outptr = fopen(FilNam, "w");
  473.             }
  474.             while (fgets(line, BUFSIZ, inptr))
  475.             {
  476.                 if (strncmp(line, Delim, strlen(Delim)) == 0)
  477.                     break;
  478.                 if (outptr != NULL)
  479.                     if (Prefix && strncmp(line, PrefixPat, Prefix) == 0)
  480.                         fputs(&line[Prefix], outptr);
  481.                     else
  482.                         fputs(line, outptr);
  483.             }
  484.             if (outptr)
  485.             {
  486.                 fclose(outptr);
  487.                 fprintf(stderr, "...done\n");
  488.             }
  489.             else
  490.                 fprintf(stderr, "...error in creating file\n");
  491.         }
  492.         else
  493.         if (strncmp(ptr, "mkdir ", 6) == 0)
  494.         {
  495.             for (nlf=ptr+6; *nlf; ++nlf)
  496.                 if (*nlf == '\n')
  497.                     *nlf = '\0';
  498.             fprintf(stderr, "Creating directory %s ...", ptr+6);
  499.             dequote(ptr+6);
  500.             i = mkdir(ptr+6);
  501.             if (i) /* then it failed */
  502.                 fprintf(stderr, "failed, errno=%u\n", errno);
  503.             else
  504.                 fprintf(stderr, "done\n");
  505.         }
  506.         else
  507.         if (strncmp(ptr, "chdir ", 6) == 0)
  508.         {
  509.             dequote(ptr+6);
  510.             chdir(ptr+6);
  511.         }
  512.         else
  513.         if (strncmp(line, "cd ", 3) == 0)
  514.         {
  515.             dequote(ptr+3);
  516.             chdir(ptr+3);
  517.         }
  518.     }
  519.     fclose(inptr);
  520. }
  521.  
  522. void getshpar(char *line, char *sea, char *par)
  523. {
  524.     register char *scr1, *scr2;
  525.  
  526.     while (*line)
  527.     {
  528.         scr1 = line;
  529.         scr2 = sea;
  530.         while (*scr1 && *scr2 && *scr1 == *scr2)
  531.         {
  532.             scr1++;
  533.             scr2++;
  534.         }
  535.         if (*scr2 == 0)
  536.         {
  537.  
  538.             // If search target is '>' then watch out for '>>'
  539.  
  540.             if (strcmp(sea,">") == 0)
  541.                 if (*scr1 == '>')
  542.                 {
  543.                     Concat = 1;
  544.                     ++scr1;
  545.                 }
  546.                 else
  547.                     Concat = 0;
  548.  
  549.             if (*scr1 == 0)
  550.             {
  551.                 *par = 0;
  552.                 return;
  553.             }
  554.             while (*scr1 == ' ' || *scr1 == '\t' ||
  555.                    *scr1 == '/' ||
  556.                    *scr1 == '\\' || *scr1 == '\'' || *scr1 == '"')
  557.                 scr1++;
  558.             while (*scr1 != 0 && *scr1 != ' ' && *scr1 != '\t' &&
  559. //                   *scr1 != '/' &&
  560.                    *scr1 != '\\' && *scr1 != '\'' && *scr1 != '"' &&
  561.                    *scr1 != '\n' && *scr1 != '\r')
  562.                 *par++ = *scr1++;
  563.             *par = 0;
  564.             return;
  565.         }
  566.         line++;
  567.     }
  568.     *par = 0;
  569. }
  570.  
  571. void dequote(char *p)
  572. {
  573.     char
  574.         *d = p,
  575.         q;
  576.  
  577.     while (isspace(*d))
  578.         ++d;
  579.     switch (*d)
  580.     {
  581. case '\'':
  582. case '"':
  583.         q = *d++;
  584.         break;
  585. default:
  586.         q = ' ';
  587.     }
  588.     while (*d && !isspace(*d) && *d != q)
  589.         *p++ = *d++;
  590.     *p = '\0';
  591. }
  592.