home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1308 / shar.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  30.2 KB  |  1,156 lines

  1. char *revision = "3.24";
  2. /*
  3. ** shar.c
  4.  
  5.   Defined functions:
  6.     Rname(file)
  7.     exit_incompat()
  8.     gen_mkdir(path)
  9.     gen_mkdir_script(path)
  10.     setTOUCH()
  11.     header(argc,argv)
  12.     helpuser()
  13.     main(argc,argv)
  14.     mode_map(mode,mode_str)
  15.     shar(file)
  16.  
  17. */
  18. /*+:EDITS:*/
  19. /*:05-10-1990-20:39-wht@n4hgf-altos does not not like at-sign in filenames */
  20. /*:05-10-1990-13:38-wht@n4hgf-add -V Vanilla mode */
  21. /*:05-07-1990-00:06-wht@n4hgf-test all mallocs for Purity Of Essence */
  22. /*:05-07-1990-00:06-wht@n4hgf-add -S switch */
  23. /*:05-05-1990-01:37-relay.EU.net!rivm!a3-dont assume vax is running BSD */
  24. /*:04-18-1990-02:01-wht@n4hgf-3.20 rhg@cps.com did all the NICE work */
  25. /*:04-17-1990-14:30-rhg@cps.com-pretty up if-then-else-fi in shar file */
  26. /*:04-17-1990-12:13-rhg@cps.com-add Split and renamed old -l to -L */
  27. /*:04-17-1990-12:13-rhg@cps.com-add -c option to shar file execution */
  28. /*:04-17-1990-11:20-rhg@cps.com-simplify TOUCH logic in shar file */
  29. /*:04-17-1990-10:27-rhg@cps.com-create setTOUCH to avoid duplicate code */
  30. /*:04-17-1990-04:43-rhg@cps.com-add missing && to commands in shar file(s) */
  31. /*:04-17-1990-02:03-rhg@cps.com-add Compress */
  32. /*:04-16-1990-17:08-rhg@cps.com-add AvoidPipes as well as code to use pipes */
  33. /*:04-03-1990-20:09-wht@n4hgf-3.11 */
  34. /*:04-01-1990-13:20-pat@rwing-correct case on M option in getopt() call */
  35. /*:04-01-1990-13:50-pat@rwing-change defaults on -v, -w to be on */
  36. /*:03-29-1990-18:23-wht@n4hgf-add automatic sequent support */
  37. /*:03-28-1990-15:56-wht@n4hgf-add mode and length net.bandwidth chrome */
  38. /*:03-28-1990-14:23-wht@n4hgf-correct some runtime diagnostics */
  39. /*:11-14-1989-02:21-wht-SHAR_EOF was botched if last file char not newline */
  40. /*:11-02-1989-14:11-wht-add touch -am */
  41.  
  42. /*
  43.  Shar puts readable text files together in a package
  44.  from which they are easy to extract.
  45.  earlier attribution wht@n4hgf has:    decvax!microsof!uw-beave!jim
  46.                                     (James Gosling at CMU)
  47. */
  48. /*
  49.  *    I have made several mods to this program:
  50.  *
  51.  *    1) the -----Cut Here-----... now preceds the script.
  52.  *    2) the cat has been changed to a sed which removes a prefix
  53.  *    character from the beginning of each line of the extracted
  54.  *    file, this prefix character is added to each line of the archived
  55.  *    files and is not the same as the first character of the
  56.  *    file delimeter.
  57.  *    3) added several options:
  58.  *        -c    - add the -----Cut Here-----... line.
  59.  *        -d'del' - change the file delimeter to del.
  60.  *        -s    - cause the resulting script to print the wc of
  61.  *              the orignal file and the wc of the extracted
  62.  *              file.
  63.  *
  64.  *                Michael A. Thompson
  65.  *                Dalhousie University
  66.  *                Halifax, N.S., Canada.
  67.  */
  68.  
  69. /*
  70.  *    I, too, have been hacking this code. This is the version on sixhub
  71.  *        bill davidsen (davidsen@sixhub.uucp)
  72.  *
  73.  *    - added support for binary files
  74.  *    - automatic creation of limited size multiple file archives,
  75.  *      each of which may be unpacked separately, and with sequence
  76.  *      checking.
  77.  *    - support for mixed text and binary files
  78.  *    - preserve file permissions
  79.  *    - restore to filename rather than pathname
  80.  *
  81.  */
  82. /*
  83.  *  One good hack deserves another ... this version generates shell
  84.  *  code which attempts to create missing directories
  85.  *  handle deviants sun, vax, pyr (pyramid), SCO XENIX/UNIX automatically
  86.  *  for sequent, add -DBSD42
  87.  *  force Verbose on
  88.  *  if unsharing system's touch Sys V compatible (allows touch -m),
  89.  *  restore file dates
  90.  *  -n switch puts an alpha "name" in header
  91.  *  -a (if also -n) puts "Submitted-by:" & "Archive-name: <name>/part##
  92.  *  use getopt
  93.  *  as well as some other chrome-plated junque
  94.  *  ...!gatech!emory!tridom!wht (wht%n4hgf@gatech.edu) Warren Tucker
  95.  *
  96.  *  3.11 - Fri Apr  6 14:21:51 EDT 1990
  97.  *  With due deference to davidsen@sixhub, more changes..... copies
  98.  *  of this, like 3.10,  were mailed to him:
  99.  *  From wht  Fri Apr  6 15:14:30 1990 remote from n4hgf
  100.  *  Received: by n4hgf.UUCP (smail2.5-UNIX/386 5.3.2)
  101.  *      id AA01781; 6 Apr 90 15:14:30 EDT (Fri)
  102.  *  Date: Fri, 6 Apr 90 15:14:30 EDT
  103.  *  X-Mailer: Mail User's Shell (6.5 4/17/89)
  104.  *  From: wht@n4hgf (Warren Tucker)
  105.  *  To: davidsen@sixhub
  106.  *  Subject: shar 3.11
  107.  *  X-Bang-Reply-to: gatech!n4hgf!wht -or- emory!tridom!n4hgf!wht
  108.  *  Reply-to: wht%n4hgf@gatech.edu
  109.  *  Message-Id: <9004061514.AA01781@n4hgf.UUCP>
  110.  *
  111.  *  1. changes suggested by pat@rwing (Pat Myrto) and silvert@cs.dal.ca
  112.  *  (Bill Silvert)
  113.  *  2. fixes to who_am_i code in who@where.c
  114.  *
  115.  *  3.20 - Wed Apr 18 01:58:32 EDT 1990
  116.  *  changes were made per edit notes by
  117. From: gatech!mailrus!uunet!cpsolv.CPS.COM!rhg (Richard H. Gumpertz)
  118.  *  ...!gatech!n4hgf!wht (wht%n4hgf@gatech.edu) Warren Tucker
  119.  *
  120.  */
  121.  
  122. #include <stdio.h>
  123. #include <sys/types.h>
  124. #include <time.h>
  125. #include <sys/stat.h>
  126.  
  127. /* assume system v unless otherwise fixed */
  128. #if (defined(pyr) || defined(vax) || defined(sequent)) && !defined(BSD42) && !defined(SYS5)
  129. #define BSD42
  130. #endif
  131. #if defined(sun)    /* this miscreant doesn't exactly fit BSD or SYSV */
  132. #undef BSD42
  133. #undef SYS5
  134. #endif
  135. #if !defined(BSD42) && !defined(sun) && !defined(SYS5)
  136. #define SYS5
  137. #endif
  138.  
  139. #if defined(sun) || defined(BSD42)
  140. #define strchr    index
  141. #define strrchr    rindex
  142. #endif
  143.  
  144. char *strchr();
  145. char *strrchr();
  146. #ifdef __STDC__ /* my concession to ANSI-pansiness */
  147. void *malloc();
  148. #else
  149. char *malloc();
  150. #endif
  151. FILE *fdopen();
  152. FILE *popen();
  153.  
  154. #define    DELIM        "SHAR_EOF"/* put after each file */
  155. #define PREFIX1        'X'    /* goes in front of each line */
  156. #define PREFIX2        'Y'    /* goes in front of each line if Delim
  157.                          * starts with PREFIX1 */
  158. #define PREFIX        (Delim[0] == PREFIX1 ? PREFIX2 : PREFIX1)
  159. #define WC            "wc -c"
  160.  
  161. int Archive_name = 0;    /* option to generate "Archive-name:" headers */
  162. int Verbose = 1;        /* option to provide append/extract feedback */
  163. int Wc_c = 1;            /* option to provide wc checking */
  164. char *Delim = DELIM;    /* pointer to delimiter string */
  165. int Cut = 0;            /* option to provide cut mark */
  166. int Binary = 0;            /* flag for binary files */
  167. int AvoidPipes = 0;        /* use temp file instead of pipe to feed uudecode, etc.
  168.                            (better error detection at expense of disk space) */
  169. int Vanilla = 0;        /* no Brown-Shirt mode */
  170. int Compress = 0;        /* run input files through compress (requires Binary) */
  171. int Mixed = 0;            /* mixed text and binary files */
  172. int eXists = 0;            /* check if file exists */
  173. int InterOW = 0;        /* interactive overwrite */
  174. int PosParam = 0;        /* allow positional parameters */
  175. int FileStrip;            /* strip directories from filenames */
  176. #ifdef    DEBUG
  177. int de_bug = 0;            /* switch for debugging on */
  178. #define DeBug(f,v) if (de_bug) printf(f, v)
  179. #else    /* normal compile */
  180. #define DeBug(f,v)        /* do nothing */
  181. #endif
  182.  
  183. FILE *fpout = stdout;
  184. char *Rname();            /* file restore name */
  185. unsigned limit = 0;
  186. int Split = 0;            /* Split files in the middle */
  187. long ftell();
  188. long TypePos;            /* position for archive type message */
  189. long EndHeadPos;        /* position for first file in the shar file */
  190. char outname[50];        /* base for output filename */
  191. char filename[50];        /* actual output filename */
  192. char *sharname = (char *)0;
  193. char *submitter = (char *)0;
  194. int filenum = 0;        /* output file # */
  195. struct stat fst;        /* check file type, access */
  196.  
  197. main(argc,argv)
  198. char **argv;
  199. {
  200. int status = 0;
  201. int stdin_file_list = 0;
  202. char *oname;
  203. int c;
  204. extern int optind;
  205. extern char *optarg;
  206.  
  207.     while((c = getopt(argc,argv,"VSvwd:btCxXcfMpPas:n:l:L:o:h")) != -1)
  208.     {
  209.         switch(c)
  210.         {
  211.         case 'V':
  212.             Vanilla = 1;
  213.             break;
  214.         case 'S':
  215.             stdin_file_list = 1;
  216.             break;
  217.         case 'v':
  218.             Verbose = 0;
  219.             break;
  220.         case 'w':
  221.             Wc_c = 0;
  222.             break;
  223.         case 'd':
  224.             Delim = optarg;
  225.             break;
  226.         case 'b': /* binary files */
  227.             Binary = 1;
  228.             Compress = 0;
  229.             break;
  230.         case 't': /* text mode */
  231.             Binary = 0;
  232.             Compress = 0;
  233.             break;
  234.         case 'C': /* Compress */
  235.             Binary = 1;
  236.             Compress = 1;
  237.             break;
  238.         case 'x': /* does the file exist */
  239.             eXists = 1;
  240.             if(InterOW || Split)
  241.                 exit_incompat();
  242.             break;
  243.         case 'X': /* does the file exist */
  244.             InterOW = 1;
  245.             if(eXists || Split)
  246.                 exit_incompat();
  247.             eXists = 1;
  248.             break;
  249.         case 'c':
  250.             Cut = 1;
  251.             break;
  252.         case 'f': /* filenames only */
  253.             FileStrip = 1;
  254.             break;
  255.         case 'M': /* mixed text and binary */
  256.             Mixed = 1;
  257.             break;
  258.         case 'p': /* allow positional parameters */
  259.             PosParam = 1;
  260.             break;
  261.         case 'P': /* use temp files instead of pipes in the shar file */
  262.             AvoidPipes = 1;
  263.             break;
  264.         case 'l': /* soft size limit in k */
  265.             if((limit = atoi(optarg)) > 1)
  266.                 --limit;
  267.             Split = 0;
  268.             DeBug("Soft limit %dk\n",limit);
  269.             break;
  270.         case 'L': /* hard size limit in k */
  271.             if(eXists)
  272.                 exit_incompat();
  273.             if((limit = atoi(optarg)) > 1)
  274.                 --limit;
  275.             Split = (limit != 0);
  276.             AvoidPipes = 1;
  277.             DeBug("Hard limit %dk\n",limit);
  278.             break;
  279.         case 'n': /* name of archive */
  280.             sharname = optarg;
  281.             break;
  282.         case 's': /* submitter */
  283.             submitter = optarg;
  284.             break;
  285.         case 'a': /* generate Archive-name: headers */
  286.             Archive_name = 1;
  287.             break;
  288.         case 'o': /* specify output file */
  289.             oname = optarg;
  290.             strcpy(outname,oname);
  291.             strcat(outname,".");
  292.             filenum = 1;
  293.             strcpy(filename,outname);
  294.             strcat(filename,"01");
  295.             fpout = fopen(filename,"w");
  296.             if(!fpout)
  297.             { /* creation error */
  298.                 perror("can't create output file");
  299.                 exit(1);
  300.             }
  301.             break;
  302. #ifdef    DEBUG
  303.         case '$': /* totally undocumented $ option, debug on */
  304.             de_bug = 1;
  305.             break;
  306. #endif
  307.         default: /* invalid option */
  308.         case 'h': /* help */
  309.             helpuser();
  310.             break;
  311.         }
  312.     }
  313.  
  314.     if(Vanilla)
  315.     {
  316.         fprintf(stderr,"Vanilla mode disabling years of progress :-)\n");
  317.         if(Binary || Mixed || Compress || PosParam)
  318.             fprintf(stderr,"WARNING: non-Text storage options overridden.\n");
  319.         Wc_c = 0;
  320.         Binary = 0;
  321.         Compress = 0;
  322.         InterOW = 0;
  323.         Mixed = 0;
  324.         PosParam = 0;
  325.         AvoidPipes = 0;
  326.     }
  327.  
  328.     if(stdin_file_list)
  329.     {
  330.         char stdin_buf[258];
  331.         argc = 0;
  332.         if(!(argv = (char **)malloc(1024 * sizeof(char *))))
  333.             goto MEMORY_ERROR;
  334.         stdin_buf[0] = 0;
  335.         while(fgets(stdin_buf,sizeof(stdin_buf),stdin))
  336.         {
  337.             if(argc == 1024)
  338.             {
  339.                 fprintf(stderr,"max files from stdin is 1024!\n");
  340.                 exit(1);
  341.             }
  342.             if(stdin_buf[0])
  343.                 stdin_buf[strlen(stdin_buf) - 1] = 0;
  344.             if(!(argv[argc] = malloc(strlen(stdin_buf) + 1)))
  345.             {
  346. MEMORY_ERROR: /* NOT likely, but free software must pure as snow! */
  347.                 fprintf(stderr,"out of memory handling stdin input at %d\n",
  348.                     argc);
  349.                 exit(1);
  350.             }
  351.             strcpy(argv[argc],stdin_buf);
  352.             argc++;
  353.             stdin_buf[0] = 0;
  354.         }
  355.         optind = 0;
  356.     }
  357.  
  358.     if(optind >= argc)
  359.     {
  360.         fprintf(stderr,"shar: No input files\n");
  361.         helpuser();
  362.         exit(1);
  363.     }
  364.  
  365.     if(Archive_name && !sharname)
  366.     {
  367.         fprintf(stderr,"shar: -n must accompany -a\n");
  368.         helpuser();
  369.         exit(1);
  370.     }
  371.  
  372.     if(!submitter)
  373.     {
  374.         if(!(submitter = malloc(128)))
  375.         {
  376.             fprintf(stderr,"memory allocation failed\n"); /* NOT likely */
  377.             exit(1);
  378.         }
  379.         who_where(submitter);
  380.     }
  381.  
  382.     if(header(argc-optind,&argv[optind]))
  383.         exit(2);
  384.  
  385.     if(InterOW)
  386.     {
  387.         Verbose = 1;
  388.         fprintf(fpout,"wish=\n");
  389.         if(Archive_name)
  390.         {
  391.             printf("PLEASE do not submit -X shars to the usenet or other\n");
  392.             printf("public networks.  They will cause problems.\n");
  393.         }
  394.     }
  395.  
  396.     EndHeadPos = ftell(fpout);
  397.  
  398.     while(optind < argc)
  399.     { /* process positional parameters and files */
  400.         if(PosParam)
  401.         {        /* allow -b and -t and -C inline */
  402.             if(strcmp(argv[optind],"-b") == 0)
  403.             { /* set binary */
  404.                 Binary = 1;
  405.                 Compress = 0;
  406.                 optind++;
  407.                 continue;
  408.             }
  409.             if(strcmp(argv[optind],"-t") == 0)
  410.             { /* set mode text */
  411.                 Binary = 0;
  412.                 Compress = 0;
  413.                 optind++;
  414.                 continue;
  415.             }
  416.             if(strcmp(argv[optind],"-C") == 0)
  417.             { /* set compress */
  418.                 Binary = 1;
  419.                 Compress = 1;
  420.                 optind++;
  421.                 continue;
  422.             }
  423.         }
  424.         status += shar(argv[optind++]);
  425.     }
  426.  
  427.     /* delete the sequence file, if any */
  428.     if(Split && filenum > 1)
  429.     {
  430.         fputs("rm -f shar3_seq_.tmp\n",fpout);
  431.         fputs("echo \"You have unpacked the last part\"\n",fpout);
  432.         if(!Verbose)
  433.             fprintf(stderr,"Created %d files\n",filenum);
  434.     }
  435.     fputs("exit 0\n",fpout);
  436.     exit(status);
  437. }
  438.  
  439. /*+-----------------------------------------------------------------------
  440.     mode_map(mode,mode_str)    build drwxrwxrwx string
  441. ------------------------------------------------------------------------*/
  442. char *
  443. mode_map(mode,mode_str)
  444. unsigned short mode;
  445. char *mode_str;
  446. {
  447. register unsigned ftype = mode & S_IFMT;
  448. register char *rtn;
  449. static char result[12];
  450.  
  451.     rtn = (mode_str == (char *)0) ? result : mode_str;
  452.  
  453.     /*          drwxrwxrwx */
  454.     /*          0123456789 */
  455.     strcpy(rtn,"----------");
  456.  
  457. #ifdef THIS_IS_NOT_NEEDED_FOR_SHAR
  458.     switch(ftype)
  459.     {
  460.         case S_IFIFO:    *rtn = 'p'; break; /* FIFO (named pipe) */
  461.         case S_IFDIR:    *rtn = 'd'; break; /* directory */
  462.         case S_IFCHR:    *rtn = 'c'; break; /* character special */
  463.         case S_IFBLK:    *rtn = 'b'; break; /* block special */
  464.         case S_IFREG:    *rtn = '-'; break; /* regular */
  465.  
  466. #if defined(sun) | defined(BSD42)
  467.         case S_IFLNK:    *rtn = 'l'; break; /* symbolic link */
  468.         case S_IFSOCK:    *rtn = 's'; break; /* socket */
  469. #endif
  470.  
  471. #if defined (SYS5)
  472.         case S_IFNAM:                        /* name space entry */
  473.             if(mode & S_INSEM)                /* semaphore */
  474.             {
  475.                 *rtn = 's';
  476.                 break;
  477.             }
  478.             if(mode & S_INSHD)                /* shared memory */
  479.             {
  480.                 *rtn = 'm';
  481.                 break;
  482.             }
  483. #endif
  484.  
  485.         default:        *rtn = '?'; break;    /* ??? */
  486.     }
  487. #endif /* THIS_IS_NOT_NEEDED_FOR_SHAR */
  488.  
  489.     if(mode & 000400) *(rtn + 1) = 'r';
  490.     if(mode & 000200) *(rtn + 2) = 'w';
  491.     if(mode & 000100) *(rtn + 3) = 'x';
  492.     if(mode & 004000) *(rtn + 3) = 's';
  493.     if(mode & 000040) *(rtn + 4) = 'r';
  494.     if(mode & 000020) *(rtn + 5) = 'w';
  495.     if(mode & 000010) *(rtn + 6) = 'x';
  496.     if(mode & 002000) *(rtn + 6) = 's';
  497.     if(mode & 000004) *(rtn + 7) = 'r';
  498.     if(mode & 000002) *(rtn + 8) = 'w';
  499.     if(mode & 000001) *(rtn + 9) = 'x';
  500.     if(mode & 001000) *(rtn + 9) = 't';
  501.  
  502.     return(rtn);
  503.  
  504. }    /* end of mode_map */
  505.  
  506. void
  507. setTOUCH()
  508. {
  509.     if(Vanilla)
  510.         return;
  511.     fputs("if touch 2>&1 | fgrep '[-amc]' > /dev/null\n",fpout);
  512.     fputs(" then TOUCH=touch\n",fpout);
  513.     fputs(" else TOUCH=true\n",fpout);
  514.     fputs("fi\n",fpout);
  515. } /* end of setTOUCH */
  516.  
  517. header(argc,argv)
  518. char **argv;
  519. {
  520. int i;
  521. int status;
  522. FILE *fpsource;    /* pipe temp */
  523. char s128[128];
  524. long now;
  525. struct tm *utc;
  526. struct tm *gmtime();
  527.  
  528.     /* see if any conflicting options */
  529.     if(limit && !filenum)
  530.     { /* can't rename what you don't have */
  531.         fprintf(stderr,"Can't use -l or -L option without -o\n");
  532.         helpuser();
  533.         return(1);
  534.     }
  535.  
  536.     for(i = 0; i < argc; i++)
  537.     { /* skip positional parameters */
  538.         if(PosParam &&
  539.             (strcmp(argv[i],"-b") == 0 ||
  540.              strcmp(argv[i],"-t") == 0 ||
  541.              strcmp(argv[i],"-C") == 0))
  542.             continue;
  543.  
  544.         /* see if access and correct type */
  545.         if(access(argv[i],4))
  546.         {
  547.             fprintf(stderr,"shar: Can't access %s\n",argv[i]);
  548.             return(1);
  549.         }
  550.  
  551.         /* get file type */
  552.         stat(argv[i],&fst);
  553.         status = fst.st_mode & S_IFMT;
  554.  
  555.         /* at this point I check to see that this is a regular file */
  556.         if(status != S_IFREG)
  557.         { /* this is not a regular file */
  558.             fprintf(stderr,"shar: %s is not a regular file\n",argv[i]);
  559.             return(1);
  560.         }
  561.     }
  562.  
  563.     if(Archive_name)
  564.     {
  565.         fprintf(fpout,"Submitted-by: %s\n",submitter);
  566.         fprintf(fpout,"Archive-name: %s%s%02d\n\n",
  567.             sharname,(strchr(sharname,'/')) ? "" : "/part",
  568.             (filenum) ? filenum : 1);
  569.     }
  570.  
  571.     if(Cut)
  572.         fputs("---- Cut Here and unpack ----\n",fpout);
  573.     fputs("#!/bin/sh\n",fpout);
  574.     if(sharname)
  575.         fprintf(fpout,"# This is %s, a shell archive (shar %s)\n",
  576.             sharname,revision);
  577.     else
  578.         fprintf(fpout,"# This is a shell archive (shar %s)\n",revision);
  579.  
  580.     time(&now);
  581.     utc = gmtime(&now);
  582.     fprintf(fpout,"# made %02d/%02d/%04d %02d:%02d UTC ",
  583.         utc->tm_mon + 1,utc->tm_mday,utc->tm_year + 1900,
  584.         utc->tm_hour,utc->tm_min);
  585.  
  586.     fputs("by ",fpout);
  587.     fputs(submitter,fpout);
  588.     fputs("\n",fpout);
  589.  
  590. #if defined(SYS5)
  591.     if(!(fpsource = popen("/bin/pwd","r")))
  592.         return(-1);
  593.     fgets(s128,sizeof(s128),fpsource);
  594.     s128[strlen(s128) - 1] = 0;
  595.     fclose(fpsource);
  596. #else
  597. #if defined(BSD42) || defined(sun)
  598.     getwd(s128);
  599. #else
  600. #include "Need_conditional_compile_fix"
  601. #endif
  602. #endif
  603.     fprintf(fpout,"# Source directory %s\n",s128);
  604.  
  605.     fprintf(fpout,"#\n# existing files %s be overwritten\n",
  606.         (eXists) ? "will NOT"
  607.                  : ((InterOW) ? "MAY" : "WILL"));
  608.     if(InterOW)
  609.         fprintf(fpout,"# The unsharer will be INTERACTIVELY queried.\n");
  610.  
  611.     if(Vanilla)
  612.     {
  613.         fprintf(fpout,
  614.         "# This format requires very little intelligence at unshar time.\n");
  615.         fputs("# ",fpout);
  616.         if(eXists)
  617.             fputs("\"if test\", ",fpout);
  618.         fputs("\"echo\" and \"sed\" will be needed.\n",fpout);
  619.     }
  620.  
  621.     if(Split)
  622.     { /* may be split, explain */
  623.         fputs("#\n",fpout);
  624.         TypePos = ftell(fpout);
  625.         fprintf(fpout,"%-75s\n%-75s\n","#","#");
  626.     }
  627.  
  628.     fputs("#\n# This shar contains:\n",fpout);
  629.     fputs("# length  mode       name\n",fpout);
  630.     fputs("# ------ ---------- ------------------------------------------\n",
  631.         fpout);
  632.     for(i = 0; i < argc; i++)
  633.     { /* output names of files but not parameters */
  634.         if(PosParam &&
  635.             (strcmp(argv[i],"-b") == 0 ||
  636.              strcmp(argv[i],"-t") == 0 ||
  637.              strcmp(argv[i],"-C") == 0))
  638.             continue;
  639.         stat(argv[i],&fst);
  640.         fst.st_mode &= ~(07000); /* turn off setuid, setgid and sticky bits */
  641.         fprintf(fpout,"# %6ld %s %s\n",fst.st_size,
  642.             mode_map(fst.st_mode,(char *)0),Rname(argv[i]));
  643.     }
  644.     fputs("#\n",fpout);
  645.  
  646.     setTOUCH();
  647.  
  648.     if(Split)
  649.     { /* now check the sequence */
  650.         fputs("if test -r shar3_seq_.tmp; then\n",fpout);
  651.         fputs("\techo \"Must unpack archives in sequence!\"\n",fpout);
  652.         fputs("\tnext=`cat shar3_seq_.tmp`; ",fpout);
  653.         fputs("echo \"Please unpack part $next next\"\n\texit 1\nfi\n",fpout);
  654.     }
  655.     return(0);
  656. }
  657.  
  658. #define MAX_MKDIR_ALREADY    128    /* ridiculously enough */
  659. char *mkdir_already[MAX_MKDIR_ALREADY];
  660. int mkdir_already_count = 0;
  661.  
  662. void
  663. gen_mkdir(path)
  664. char *path;
  665. {
  666. register int ialready;
  667. char *cptr;
  668.  
  669. /* if already generated code for this dir creation, dont do again */
  670.     for(ialready = 0; ialready < mkdir_already_count; ialready++)
  671.     {
  672.         if(!strcmp(path,mkdir_already[ialready]))
  673.             return;
  674.     }
  675.  
  676. /* haven't done this one */
  677.     if(mkdir_already_count == MAX_MKDIR_ALREADY)
  678.     {
  679.         fprintf(stderr,"too many directories for mkdir generation\n");
  680.         exit(255);
  681.     }
  682.     if(!(cptr = mkdir_already[mkdir_already_count++] = malloc(strlen(path)+1)))
  683.     {
  684.         fprintf(stderr,"out of memory for mkdir generation\n");
  685.         exit(255);
  686.     }
  687.     strcpy(cptr,path);
  688.  
  689. /* generate the text */
  690.     fprintf(fpout,"if test ! -d '%s'; then\n",path);
  691.     if(Verbose)
  692.         fprintf(fpout,"    echo \"x - creating directory %s\"\n",path);
  693.     fprintf(fpout,"    mkdir '%s'\n",path);
  694.     fprintf(fpout,"fi\n");
  695.  
  696. }    /* end of gen_mkdir */
  697.  
  698. void
  699. gen_mkdir_script(path)
  700. register char *path;
  701. {
  702. register char *cptr;
  703.  
  704.     for(cptr = strchr(path,'/'); cptr; cptr = strchr(cptr + 1,'/'))
  705.     {
  706.         /* avoid empty string if leading or double '/' */
  707.         if(cptr == path || *(cptr - 1) == '/')
  708.             continue;
  709.         /* omit '.' */
  710.         if((*(cptr - 1) == '.') && ((cptr == path + 1) || (*(cptr - 2) == '/')))
  711.             continue;
  712.         *cptr = 0;                /* temporarily terminate string */
  713.         gen_mkdir(path);
  714.         *cptr = '/';
  715.     }
  716. }    /* end of gen_mkdir_script */
  717.  
  718. shar(file)
  719. char *file;
  720. {
  721. char line[BUFSIZ];
  722. FILE *fpsource;
  723. long cursize,remaining,ftell();
  724. int split = 0;        /* file split flag */
  725. char *filetype;        /* text or binary */
  726. char *RstrName;        /* name for restore */
  727. struct tm *lt;
  728. char *filename_base;
  729.  
  730.     /* get file size, dates, and mode for later */
  731.     stat(file,&fst);
  732.  
  733.     /* if limit set, get the current output length */
  734.     if(limit)
  735.     {
  736.         cursize = ftell(fpout);
  737.         remaining = (limit * 1024L) - cursize;
  738.         DeBug("In shar: remaining size %ld\n",remaining);
  739.         
  740.         if(!Split && cursize > EndHeadPos &&
  741.             (Binary ? fst.st_size + fst.st_size/3 : fst.st_size) > remaining)
  742.         { /* change to another file */
  743.             DeBug("Newfile, remaining %ld, ",remaining);
  744.             DeBug("limit still %d\n",limit);
  745.  
  746.             fprintf(fpout,
  747.                 "echo \"End of part %d, continue with part %d\"\n",
  748.                 filenum,filenum + 1);
  749.             fprintf(fpout,"exit 0\n");
  750.  
  751.             fclose(fpout);
  752.  
  753.             /* form the next filename */
  754.             sprintf(filename,"%s%02d",outname,++filenum);
  755.             fpout = fopen(filename,"w");
  756.  
  757.             if(Archive_name)
  758.             {
  759.                 fprintf(fpout,"Submitted-by: %s\n",submitter);
  760.                 fprintf(fpout,"Archive-name: %s%s%02d\n\n",
  761.                     sharname,(strchr(sharname,'/')) ? "." : "/part",
  762.                     (filenum) ? filenum : 1);
  763.             }
  764.  
  765.             if(Cut)
  766.                 fputs("---- Cut Here and unpack ----\n",fpout);
  767.             if(!(filename_base = strrchr(filename,'/')))
  768.                 filename_base = filename;
  769.             else
  770.                 filename_base++;
  771.  
  772.             fprintf(fpout,"#!/bin/sh\n");
  773.             fprintf(fpout,"# This is part %02d of %s\n",
  774.                 filenum,(sharname) ? sharname : "a multipart archive");
  775.  
  776.             setTOUCH();
  777.  
  778.             EndHeadPos = ftell(fpout);
  779.         }
  780.     }
  781.  
  782.     /* determine the name to use for restore */
  783.     RstrName = Rname(file);
  784.  
  785.     fputs("# ============= ",fpout);
  786.     fputs(RstrName,fpout);
  787.     fputs(" ==============\n",fpout);
  788.  
  789.     gen_mkdir_script(RstrName);
  790.  
  791.     /* if mixed, determine the file type */
  792.     if(Mixed)
  793.     {
  794.         int count;
  795.         sprintf(line,"file %s | egrep -c \"text|shell\"",file);
  796.         fpsource = popen(line,"r");
  797.         fscanf(fpsource,"%d",&count);
  798.         pclose(fpsource);
  799.         Binary = (count != 1);
  800.     }
  801.  
  802.     if(Binary)
  803.     { /* fork a uuencode process */
  804.         static int pid,pipex[2];
  805.  
  806.         pipe(pipex);
  807.         fflush(fpout);
  808.  
  809.         if(pid = fork())
  810.         { /* parent, create a file to read */
  811.             if(pid < 0)
  812.             {
  813.                 fprintf(stderr,"could not fork!\n");
  814.                 exit(1);
  815.             }
  816.             close(pipex[1]);
  817.             fpsource = fdopen(pipex[0],"r");
  818.             filetype = (Compress ? "Compressed" : "Binary");
  819.         }
  820.         else
  821.         { /* start writing the pipe with encodes */
  822.             FILE *outptr;
  823.  
  824.             if(Compress)
  825.             {
  826.                 sprintf(line, "compress < %s", file);
  827.                 fpsource = popen(line, "r");
  828.             }
  829.             else
  830.                 fpsource = fopen(file, "rb");
  831.             outptr = fdopen(pipex[1],"w");
  832.             fprintf(outptr,"begin 600 %s\n",
  833.                 (Compress ? "shar3_cmp_.tmp" : RstrName));
  834.             encode(fpsource,outptr);
  835.             fprintf(outptr,"end\n");
  836.             if(Compress)
  837.                 pclose(fpsource);
  838.             else
  839.                 fclose(fpsource);
  840.             exit(0);
  841.         }
  842.     }
  843.     else
  844.     {
  845.         fpsource = fopen(file,"r");
  846.         filetype = "Text";
  847.     }
  848.  
  849.     if(fpsource)
  850.     {
  851.         /* protect existing files */
  852.         if(eXists)
  853.         {
  854.             fprintf(fpout,"if test X\"$1\" != X\"-c\" -a -f '%s'; then\n",
  855.                 RstrName);
  856.             if(InterOW)
  857.             {
  858.                 fprintf(fpout,"\tcase $wish in\n");
  859.                 fprintf(fpout,"\tA*|a*) echo x - overwriting '%s';;\n",
  860.                     RstrName);
  861.                 fprintf(fpout,
  862.         "\t*) echo \"? - overwrite '%s' -- [No], [Y]es, [A]ll, [Q]uit? \"\n",
  863.                     RstrName);
  864.                 fprintf(fpout,"\t\tread wish;;\n");
  865.                 fprintf(fpout,"\tesac\n");
  866.                 fprintf(fpout,"\tcase $wish in\n");
  867.                 fprintf(fpout,"\tQ*|q*) echo aborted; exit 86;;\n");
  868.                 fprintf(fpout,"\tA*|a*|Y*|y*) x=Y;;\n");
  869.                 fprintf(fpout,"\t*) x=N;;\n");
  870.                 fprintf(fpout,"\tesac\n");
  871.                 fprintf(fpout,"else\n");
  872.                 fprintf(fpout,"\tx=Y\n");
  873.                 fprintf(fpout,"fi\n");
  874.                 fprintf(fpout,"if test $x != Y; then\n");
  875.                 fprintf(fpout,"\techo x - skipping '%s'\n",RstrName);
  876.             }
  877.             else
  878.                 fprintf(fpout,"\techo \"File already exists: skipping '%s'\"\n",
  879.                     RstrName);
  880.             fprintf(fpout,"else\n");
  881.         }
  882.  
  883.         fprintf(stderr,"shar: saving %s (%s)\n",file,filetype);
  884.         if(Verbose)
  885.         { /* info on archive and unpack */
  886.             fprintf(fpout,"echo \"x - extracting %s (%s)\"\n",
  887.                 RstrName,filetype);
  888.         }
  889.         if(Binary)
  890.         { /* run sed through uudecode (via temp file if might get split) */
  891.             fprintf(fpout, "sed 's/^%c//' << '%s' %s &&\n",
  892.                    PREFIX,Delim,
  893.                 (AvoidPipes ? "> shar3_tmp_.tmp" : "| uudecode"));
  894.         }
  895.         else
  896.         { /* just run it into the file */
  897.             fprintf(fpout,"sed 's/^%c//' << '%s' > %s &&\n",
  898.                 PREFIX,Delim,RstrName);
  899.         }
  900.         while(fgets(line,BUFSIZ,fpsource))
  901.         { /* output a line and test the length */
  902.             fprintf(fpout,"%c%s",PREFIX,line);
  903.             if(Split && (remaining -= strlen(line) + 2) < 0)
  904.             { /* change to another file */
  905.                 DeBug("Newfile, remaining %ld, ",remaining);
  906.                 DeBug("limit still %d\n",limit);
  907.  
  908.                 if(line[strlen(line) - 1] != '\n')
  909.                     fputc('\n',fpout);
  910.  
  911.                 fprintf(fpout,"%s\n",Delim);
  912.                 if(Verbose)
  913.                 { /* output some reassurance */
  914.                     fprintf(fpout,
  915.                         "echo \"End of %s part %d\"\n",
  916.                             (sharname) ? sharname : "",filenum);
  917.                     fprintf(fpout,
  918.                         "echo \"File %s is continued in part %d\"\n",
  919.                         RstrName,filenum + 1);
  920.                 }
  921.                 else
  922.                     fprintf(fpout,
  923.                         "echo \"End of part %d, continue with part %d\"\n",
  924.                         filenum,filenum + 1);
  925.                 fprintf(fpout,"echo \"%d\" > shar3_seq_.tmp\n",filenum + 1);
  926.                 fprintf(fpout,"exit 0\n");
  927.  
  928.                 if(filenum == 1)
  929.                 { /* rewrite the info lines on the firstheader */
  930.                     fseek(fpout,TypePos,0);
  931.                     fprintf(fpout,"%-75s\n%-75s\n",
  932.                         "# This is part 1 of a multipart archive",
  933.                         "# do not concatenate these parts, unpack them in order with /bin/sh");
  934.                 }
  935.                 fclose(fpout);
  936.  
  937.                 /* form the next filename */
  938.                 sprintf(filename,"%s%02d",outname,++filenum);
  939.                 fpout = fopen(filename,"w");
  940.  
  941.                 if(Archive_name)
  942.                 {
  943.                     fprintf(fpout,"Submitted-by: %s\n",submitter);
  944.                     fprintf(fpout,"Archive-name: %s%s%02d\n\n",
  945.                         sharname,(strchr(sharname,'/')) ? "." : "/part",
  946.                         (filenum) ? filenum : 1);
  947.                 }
  948.  
  949.                 if(Cut)
  950.                     fputs("---- Cut Here and unpack ----\n",fpout);
  951.                 if(!(filename_base = strrchr(filename,'/')))
  952.                     filename_base = filename;
  953.                 else
  954.                     filename_base++;
  955.  
  956.                 fprintf(fpout,"#!/bin/sh\n");
  957.                 fprintf(fpout,
  958.                     "# this is %s (part %d of %s)\n",
  959.                     filename_base,
  960.                     filenum,
  961.                     (sharname) ? sharname : "a multipart archive");
  962.                 fputs("# do not concatenate these parts, ",fpout);
  963.                 fputs("unpack them in order with /bin/sh\n",fpout);
  964.                 fprintf(fpout,"# file %s continued\n#\n",RstrName);
  965.  
  966.                 setTOUCH();
  967.                 
  968.                 fputs("if test ! -r shar3_seq_.tmp; then\n",fpout);
  969.                 fputs("\techo \"Please unpack part 1 first!\"\n",fpout);
  970.                 fputs("\texit 1\nfi\n",fpout);
  971.                 fputs("(read Scheck\n",fpout);
  972.                 fprintf(fpout,
  973.                       " if test \"$Scheck\" != %d; then\n",filenum);
  974.                 fputs("\techo \"Please unpack part $Scheck next!\"\n",
  975.                     fpout);
  976.                 fputs("\texit 1\n",fpout);
  977.                 fputs(" else\n\texit 0\n fi\n",fpout);
  978.                 fputs(") < shar3_seq_.tmp || exit 1\n",fpout);
  979.  
  980.                 if(Verbose)
  981.                 { /* keep everybody informed */
  982.                     fprintf(stderr,"Starting file %s\n",filename);
  983.                     fprintf(fpout,
  984.                         "echo \"x - Continuing file %s\"\n",RstrName);
  985.                 }
  986.                 fprintf(fpout,
  987.                     "sed 's/^%c//' << '%s' >> %s &&\n",
  988.                     PREFIX,Delim,
  989.                     (Binary ? "shar3_tmp_.tmp" : RstrName));
  990.                 remaining = limit * 1024L;
  991.                 split = 1;
  992.             }
  993.         }
  994.  
  995.         (void) fclose(fpsource);
  996.  
  997.         if(line[strlen(line) - 1] != '\n')
  998.             fputc('\n',fpout);
  999.  
  1000.         fprintf(fpout,"%s\n",Delim);
  1001.         if(split && Verbose)
  1002.             fprintf(fpout,
  1003.                 "echo \"File %s is complete\" &&\n",RstrName);
  1004.  
  1005.         /* if this file was uuencoded w/Split, decode it and drop the temp */
  1006.         if(Binary && AvoidPipes)
  1007.         {
  1008.             if(Verbose)
  1009.                 fprintf(fpout,"echo \"uudecoding file %s\" &&\n",RstrName);
  1010.             fprintf(fpout,
  1011.                 "uudecode < shar3_tmp_.tmp && rm -f shar3_tmp_.tmp &&\n");
  1012.         }
  1013.  
  1014.         /* if this file was compressed, uncompress it and drop the temp */
  1015.         if(Compress)
  1016.         {
  1017.             if(Verbose)
  1018.                 fprintf(fpout,"echo \"uncompressing file %s\" &&\n",RstrName);
  1019.             fprintf(fpout,
  1020.                 "compress -d < shar3_cmp_.tmp > %s && rm -f shar3_cmp_.tmp &&\n",
  1021.                 RstrName);
  1022.         }
  1023.  
  1024.         if(!Vanilla)
  1025.         {
  1026.             /* set the dates as they were */
  1027.             lt = localtime(&fst.st_mtime);
  1028.             fprintf(fpout,"$TOUCH -am %02d%02d%02d%02d%02d %s &&\n",
  1029.                 lt->tm_mon + 1,lt->tm_mday,lt->tm_hour,lt->tm_min,
  1030.                 lt->tm_year,RstrName);
  1031.  
  1032.             /* set the permissions as they were */
  1033.             fprintf(fpout,"chmod %04o %s ||\n",
  1034.                 fst.st_mode & 00777,RstrName);
  1035.  
  1036.             /* report an error if any of the above failed */
  1037.             fprintf(fpout,"echo \"restore of %s failed\"\n",RstrName);
  1038.  
  1039.             if(Wc_c)
  1040.             { /* validate the transferred file */
  1041.                 FILE *pfp;
  1042.                 char command[BUFSIZ];
  1043.  
  1044.                 sprintf(command,"%s %s",WC,file);
  1045.                 if((pfp = popen(command,"r")))
  1046.                 {
  1047.                     char wc[BUFSIZ];
  1048.  
  1049.                     fscanf(pfp,"%s",wc);
  1050.                     fprintf(fpout,"set `%s %s`;Wc_c=$1\n",
  1051.                         WC,RstrName);
  1052.                     fprintf(fpout,
  1053.                         "if test \"$Wc_c\" != \"%s\"; then\n",wc);
  1054.                     fprintf(fpout,
  1055.                         "\techo original size %s, current size $Wc_c\nfi\n",
  1056.                         wc);
  1057.                     pclose(pfp);
  1058.                 }
  1059.             }
  1060.         }
  1061.  
  1062.         /* if the exists option is in place close the if */
  1063.         if(eXists)
  1064.             fprintf(fpout,"fi\n");
  1065.  
  1066.         return(0);
  1067.     }
  1068.     else
  1069.     {
  1070.         fprintf(stderr,"shar: Can't open %s (%s): ",file,filetype);
  1071.         perror("");
  1072.         return(1);
  1073.     }
  1074. }
  1075.  
  1076. char *
  1077. Rname(file)
  1078. register char *file;
  1079. {
  1080.     register char *RstrName;
  1081.  
  1082.     if(FileStrip)
  1083.     { /* use just the filename */
  1084.         RstrName = file+strlen(file);
  1085.         while(RstrName > file && *RstrName != '/')
  1086.             RstrName--;
  1087.         if(*RstrName == '/') RstrName++;
  1088.     }
  1089.     else
  1090.         RstrName = file;
  1091.     if(!strncmp(RstrName,"./",2))
  1092.         RstrName += 2;
  1093.     return(RstrName);
  1094. }
  1095.  
  1096. /*****************************************************************
  1097.  |  exit_incompat - incompatible options
  1098.  ****************************************************************/
  1099.  
  1100. exit_incompat()
  1101. {
  1102.        fputs("You may only specify one of -L, -X or -x\n",stderr);
  1103.     exit(1);
  1104. }
  1105.  
  1106. char *helpinfo[] =
  1107. {
  1108.     "-V  produce \"vanilla\" shars demanding little of the unshar environment",
  1109.     "-v  verbose messages OFF while executing",
  1110.     "-w  don't check with 'wc -c' after unpack",
  1111.     "-n  Name of archive (documentation)",
  1112.     "-a  Generate Submitted-by: & Archive-name: headers",
  1113.     "-s  override automatically determined submitter name",
  1114.     "-x  don't overwrite existing files",
  1115.     "-X  interactively overwrite existing files (NOT FOR NET SHARS)",
  1116.     "-b  treat all files as binary, use uuencode",
  1117.     "-t  treat all files as text (default)",
  1118.     "-C  compress and uuencode all files",
  1119.     "-p  allow positional parameter options. The options \"-b\" and \"-t\"",
  1120.     "    and \"-C\" may be embedded, and files to the right of the",
  1121.     "    option will be processed in the specified mode",
  1122.     "-M  mixed mode. Determine if the files are text or",
  1123.     "    binary and archive correctly.",
  1124.     "-P  use temp files instead of pipes in the shar file",
  1125.     "-c  start the shar with a cut line",
  1126.     "-f  restore by filename only, rather than path",
  1127.     "-dXXX   use XXX to delimit the files in the shar",
  1128.     "-oXXX   (or -o XXX) output to file XXX.01 thru XXX.nn",
  1129.     "-lXX    limit output file size to XXk bytes (but don't split files)",
  1130.     "-LXX    limit output file size to XXk bytes (may split files)",
  1131.     "-S      read files to wrap from stdin, ignoring argument line",
  1132.     "\nThe -S option reads filenames one per line from stdin; input",
  1133.     "format must be similar to 'find' output, except that if -p",
  1134.     "is specified, -b, -t or -C may be used (on lines by themselves)",
  1135.     "e.g., find . -type f -print | sort | shar -C -L50 -o /tmp/big",
  1136.     "\nThe 'o' option is required if the 'l' or 'L' option is used",
  1137.     "The 'n' option is required if the 'a' option is used",
  1138.     "The 'x' and 'L' options are incompatible",
  1139.     "\n-a generates sharname/part## headers. If the -a argument contains",
  1140.     "a '/', then /patch is not appended",
  1141.     "The automatic submitter name is trivial: essentially `whoami`@`uname`",
  1142.     (char *)0
  1143. };
  1144.  
  1145. helpuser()
  1146. {                /* output a command format message */
  1147.     register char **ptr;
  1148.     fprintf(stderr,
  1149.         "shar %s\nusage: shar [ options ] file [ file1 ... ] ]\n",revision);
  1150.     for(ptr = helpinfo; *ptr; ptr++)
  1151.         fprintf(stderr,"%s\n",*ptr);
  1152.  
  1153.     exit(1);
  1154. }
  1155. /* vi: set tabstop=4 shiftwidth=4: */
  1156.