home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / sharutils-4.1-src.tgz / tar.out / fsf / sharutils / shar.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  46KB  |  1,879 lines

  1. /* Handle so called `shell archives'.
  2.    Copyright (C) 1994 Free Software Foundation, Inc.
  3.   
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.   
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.   
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17. */
  18.  
  19. #include "system.h"
  20.  
  21. static const char *cut_mark_line
  22.   = "---- Cut Here and feed the following to sh ----\n";
  23.  
  24. /* Delimiter to put after each file.  */
  25. #define    DEFAULT_HERE_DELIMITER "SHAR_EOF"
  26.  
  27. /* Character which goes in front of each line.  */
  28. #define DEFAULT_LINE_PREFIX_1 'X'
  29.  
  30. /* Character which goes in front of each line if here_delimiter[0] ==
  31.    DEFAULT_LINE_PREFIX_1.  */
  32. #define DEFAULT_LINE_PREFIX_2 'Y'
  33.  
  34. /* Shell command able to count characters from its standard input.  */
  35. #define CHARACTER_COUNT_COMMAND "wc -c <"
  36.  
  37. /* Maximum length for a text line before it is considered binary.  */
  38. #define MAXIMUM_NON_BINARY_LINE 200
  39.  
  40. /* System related declarations.  */
  41.  
  42. #include <ctype.h>
  43. #include <time.h>
  44. #include <sys/stat.h>
  45.  
  46. struct tm *localtime ();
  47.  
  48. #if !NO_WALKTREE
  49.  
  50. /* Declare directory reading routines and structures.  */
  51.  
  52. #ifdef __MSDOS__
  53. # include "msd_dir.h"
  54. # define NAMLEN(dirent) ((dirent)->d_namlen)
  55. #else
  56. # if HAVE_DIRENT_H
  57. #  include <dirent.h>
  58. #  define NAMLEN(dirent) (strlen((dirent)->d_name))
  59. # else
  60. #  define dirent direct
  61. #  define NAMLEN(dirent) ((dirent)->d_namlen)
  62. #  if HAVE_SYS_NDIR_H
  63. #   include <sys/ndir.h>
  64. #  endif
  65. #  if HAVE_SYS_DIR_H
  66. #   include <sys/dir.h>
  67. #  endif
  68. #  if HAVE_NDIR_H
  69. #   include <ndir.h>
  70. #  endif
  71. # endif
  72. #endif
  73.  
  74. DIR *opendir ();
  75. struct dirent *readdir ();
  76.  
  77. #endif /* !NO_WALKTREE */
  78.  
  79. /* Option variables.  */
  80.  
  81. #include "getopt.h"
  82.  
  83. /* No Brown-Shirt mode.  */
  84. static int vanilla_operation_mode = 0;
  85.  
  86. /* Mixed text and binary files.  */
  87. static int mixed_uuencoded_file_mode = -1;
  88.  
  89. /* Flag for binary files.  */
  90. static int uuencoded_file_mode = -1;
  91.  
  92. /* Run input files through gzip (requires uuencoded_file_mode).  */
  93. static int gzipped_file_mode = -1;
  94.  
  95. /* -N option to gzip.  */
  96. static int gzip_compression_level = 9;
  97.  
  98. /* Run input files through compress (requires uuencoded_file_mode).  */
  99. static int compressed_file_mode = -1;
  100.  
  101. /* -bN option to compress */
  102. static int bits_per_compressed_byte = 12;
  103.  
  104. /* Generate $shar_touch commands.  */
  105. static int timestamp_mode = 1;
  106.  
  107. /* Option to provide wc checking.  */
  108. static int character_count_mode = 1;
  109.  
  110. /* Use temp file instead of pipe to feed uudecode.  This gives better
  111.    error detection, at expense of disk space.  This is also necessary for
  112.    those versions of uudecode unwilling to read their standard input.  */
  113. static int inhibit_piping_mode = 0;
  114.  
  115. /* Character to get at the beginning of each line.  */
  116. static int line_prefix;
  117.  
  118. /* Option to generate "Archive-name:" headers.  */
  119. static int net_headers_mode = 0;
  120.  
  121. /* Documentation name for archive.  */
  122. static char *archive_name = NULL;
  123.  
  124. /* Option to provide append feedback at shar time.  */
  125. static int quiet_mode = 0;
  126.  
  127. /* Option to provide extract feedback at unshar time.  */
  128. static int quiet_unshar_mode = 0;
  129.  
  130. /* Pointer to delimiter string.  */
  131. static const char *here_delimiter = DEFAULT_HERE_DELIMITER;
  132.  
  133. /* Value of strlen (here_delimiter).  */
  134. static size_t here_delimiter_length;
  135.  
  136. /* Use line_prefix even when first char does not force it.  */
  137. static int mandatory_prefix_mode = 0;
  138.  
  139. /* Option to provide cut mark.  */
  140. static int cut_mark_mode = 0;
  141.  
  142. /* Check if file exists.  */
  143. static int check_existing_mode = 1;
  144.  
  145. /* Interactive overwrite.  */
  146. static int query_user_mode = 0;
  147.  
  148. /* Allow positional parameters.  */
  149. static int intermixed_parameter_mode = 0;
  150.  
  151. /* Strip directories from filenames.  */
  152. static int basename_mode;
  153.  
  154. /* Switch for debugging on.  */
  155. #if DEBUG
  156. static int debugging_mode = 0;
  157. #endif
  158.  
  159. /* Split files in the middle.  */
  160. static int split_file_mode = 0;
  161.  
  162. /* File size limit in kilobytes.  */
  163. static unsigned file_size_limit = 0;
  164.  
  165. /* Other global variables.  */
  166.  
  167. /* The name this program was run with. */
  168. const char *program_name;
  169.  
  170. /* If non-zero, display usage information and exit.  */
  171. static int show_help = 0;
  172.  
  173. /* If non-zero, print the version on standard output and exit.  */
  174. static int show_version = 0;
  175.  
  176. /* File onto which the shar script is being written.  */
  177. static FILE *output = NULL;
  178.  
  179. /* Position for archive type message.  */
  180. static long archive_type_position;
  181.  
  182. /* Position for first file in the shar file.  */
  183. static long first_file_position;
  184.  
  185. /* Base for output filename.  */
  186. static char output_base_name[50];
  187.  
  188. /* Actual output filename.  */
  189. static char output_filename[50];
  190.  
  191. static char *submitter_address = NULL;
  192.  
  193. /* Output file ordinal.  FIXME: also flag for -o.  */
  194. static int part_number = 0;
  195.  
  196. /* Table saying whether each character is binary or not.  */
  197. static unsigned char byte_is_binary[256];
  198.  
  199. /* For checking file type and access modes.  */
  200. static struct stat struct_stat;
  201.  
  202. #if DEBUG
  203. # define DEBUG_PRINT(Format, Value) \
  204.     if (debugging_mode) printf(Format, Value)
  205. #else
  206. # define DEBUG_PRINT(Format, Value)
  207. #endif
  208.  
  209. static void usage _((int));
  210.  
  211. /* Walking tree routines.  */
  212.  
  213. /* Define a type just for easing ansi2knr's life.  */
  214. typedef int (*walker_t) _((const char *, const char *));
  215.  
  216. #if !NO_WALKTREE
  217.  
  218. /*--------------------------------------------------------------------------.
  219. | Recursively call ROUTINE on each entry, down the directory tree.  NAME    |
  220. | is the path to explore.  RESTORE_NAME is the name that will be later        |
  221. | relative to the unsharing directory.  ROUTINE may also assume            |
  222. | struct_stat is set, it accepts updated values for NAME and RESTORE_NAME.  |
  223. `--------------------------------------------------------------------------*/
  224.  
  225. static int
  226. walkdown (walker_t routine, const char *local_name, const char *restore_name)
  227. {
  228.   DIR *directory;        /* directory being scanned */
  229.   struct dirent *entry;        /* current entry in directory */
  230.   int status;            /* status to return */
  231.  
  232.   char *local_name_copy;    /* writeable copy of local_name */
  233.   size_t local_name_length;    /* number of characters in local_name_copy */
  234.   size_t sizeof_local_name;    /* allocated size of local_name_copy */
  235.  
  236.   char *restore_name_copy;    /* writeable copy of restore_name */
  237.   size_t restore_name_length;    /* number of characters in restore_name_copy */
  238.   size_t sizeof_restore_name;    /* allocated size of restore_name_copy */
  239.  
  240.   if (stat (local_name, &struct_stat))
  241.     {
  242.       error (0, errno, local_name);
  243.       return 1;
  244.     }
  245.  
  246.   if (!S_ISDIR (struct_stat.st_mode & S_IFMT))
  247.     return (*routine) (local_name, restore_name);
  248.  
  249.   if (directory = opendir (local_name), !directory)
  250.     {
  251.       error (0, errno, local_name);
  252.       return 1;
  253.     }
  254.  
  255.   status = 0;
  256.  
  257.   local_name_copy = xstrdup (local_name);
  258.   local_name_length = strlen (local_name_copy);
  259.   sizeof_local_name = local_name_length + 1;
  260.  
  261.   restore_name_copy = xstrdup (restore_name);
  262.   restore_name_length = strlen (restore_name_copy);
  263.   sizeof_restore_name = restore_name_length + 1;
  264.  
  265.   while (!status && (entry = readdir (directory), entry))
  266.     if (strcmp (entry->d_name, ".") && strcmp (entry->d_name, ".."))
  267.       {
  268.     int added_size = 1 + NAMLEN (entry);
  269.  
  270.     /* Update file names, reallocating them as required.  */
  271.  
  272.     if (local_name_length + added_size + 1 > sizeof_local_name)
  273.       {
  274.         sizeof_local_name = local_name_length + added_size + 1;
  275.         local_name_copy = (char *)
  276.           xrealloc (local_name_copy, sizeof_local_name);
  277.       }
  278.     sprintf (local_name_copy + local_name_length, "/%s", entry->d_name);
  279.  
  280.     if (restore_name_length + added_size + 1 > sizeof_restore_name)
  281.       {
  282.         sizeof_restore_name = restore_name_length + added_size + 1;
  283.         restore_name_copy = (char *)
  284.           xrealloc (restore_name_copy, sizeof_restore_name);
  285.       }
  286.     sprintf (restore_name_copy + restore_name_length, "/%s",
  287.          entry->d_name);
  288.  
  289.     /* Avoid restoring `./xxx' when shar'ing `.'.  */
  290.  
  291.     if (!strncmp (restore_name, "./", 2))
  292.       {
  293.         strcpy (restore_name_copy, restore_name_copy + 2);
  294.         restore_name_length -= 2;
  295.       }
  296.  
  297.     status = walkdown (routine, local_name_copy, restore_name_copy);
  298.       }
  299.  
  300.   /* Clean up.  */
  301.  
  302.   if (sizeof_local_name > 0)
  303.     free (local_name_copy);
  304.   if (sizeof_restore_name > 0)
  305.     free (restore_name_copy);
  306.  
  307. #if CLOSEDIR_VOID
  308.   closedir (directory);
  309. #else
  310.   if (closedir (directory))
  311.     {
  312.       error (0, errno, local_name);
  313.       return 1;
  314.     }
  315. #endif
  316.  
  317.   return status;
  318. }
  319.  
  320. #endif /* !NO_WALKTREE */
  321.  
  322. /*------------------------------------------------------------------.
  323. | Walk through the directory tree, calling ROUTINE for each entry.  |
  324. | ROUTINE may also assume struct_stat is set.                |
  325. `------------------------------------------------------------------*/
  326.  
  327. static int
  328. walktree (walker_t routine, const char *local_name)
  329. {
  330.   const char *restore_name;
  331.   char *local_name_copy;
  332.   char *cursor;
  333.   int status;
  334.  
  335.   /* Remove crumb at end.  */
  336.  
  337.   local_name_copy = xstrdup (local_name);
  338.   cursor = local_name_copy + strlen (local_name_copy) - 1;
  339.   while (*cursor == '/' && cursor > local_name_copy)
  340.     *cursor-- = '\0';
  341.  
  342.   /* Remove crumb at beginning.  */
  343.  
  344.   if (basename_mode)
  345.     restore_name = basename (local_name_copy);
  346.   else if (!strncmp (local_name_copy, "./", 2))
  347.     restore_name = local_name_copy + 2;
  348.   else
  349.     restore_name = local_name_copy;
  350.  
  351. #if NO_WALKTREE
  352.  
  353.   /* Just act on current entry.  */
  354.  
  355.   if (status = stat (local_name_copy, &struct_stat), status)
  356.     error (0, errno, local_name_copy);
  357.   else
  358.     status = (*routine) (local_name_copy, restore_name);
  359.  
  360. #else
  361.  
  362.   /* Walk recursively.  */
  363.  
  364.   status = walkdown (routine, local_name_copy, restore_name);
  365.  
  366. #endif
  367.  
  368.   free (local_name_copy);
  369.   return status;
  370. }
  371.  
  372. /* Generating parts of shar file.  */
  373.  
  374. /*---------------------------------------------------------------------.
  375. | Build a `drwxrwxrwx' string corresponding to MODE into MODE_STRING.  |
  376. `---------------------------------------------------------------------*/
  377.  
  378. static char *
  379. mode_string (unsigned mode)
  380. {
  381.   static char result[12];
  382.  
  383.   strcpy (result, "----------");
  384.  
  385.   if (mode & 00400)
  386.     result[1] = 'r';
  387.   if (mode & 00200)
  388.     result[2] = 'w';
  389.   if (mode & 00100)
  390.     result[3] = 'x';
  391.   if (mode & 04000)
  392.     result[3] = 's';
  393.   if (mode & 00040)
  394.     result[4] = 'r';
  395.   if (mode & 00020)
  396.     result[5] = 'w';
  397.   if (mode & 00010)
  398.     result[6] = 'x';
  399.   if (mode & 02000)
  400.     result[6] = 's';
  401.   if (mode & 00004)
  402.     result[7] = 'r';
  403.   if (mode & 00002)
  404.     result[8] = 'w';
  405.   if (mode & 00001)
  406.     result[9] = 'x';
  407.  
  408.   return result;
  409. }
  410.  
  411. /*-----------------------------------------------------------------------.
  412. | Generate shell code which, at *unshar* time, will study the properties |
  413. | of the unpacking system and set some variables accordingly.         |
  414. `-----------------------------------------------------------------------*/
  415.  
  416. static void
  417. generate_configure (void)
  418. {
  419.   if (query_user_mode)
  420.     if (vanilla_operation_mode)
  421.       fputs ("\
  422. shar_tty= shar_n= shar_c='\n\
  423. '\n",
  424.          output);
  425.     else
  426.       {
  427.  
  428.     /* Check if /dev/tty exists.  If yes, define shar_tty to
  429.        `/dev/tty', else, leave it empty.  */
  430.  
  431.     fputs ("\
  432. if test -n \"`ls /dev/tty 2>/dev/null`\"; then\n\
  433.   shar_tty=/dev/tty\n\
  434. else\n\
  435.   shar_tty=\n\
  436. fi\n",
  437.            output);
  438.  
  439.     /* Try to find a way to echo a message without newline.  Set
  440.        shar_n to `-n' or nothing for an echo option, and shar_c to
  441.        `\c' or nothing for a string terminator.  */
  442.  
  443.     fputs ("\
  444. if (echo \"testing\\c\"; echo 1,2,3) | grep c >/dev/null; then\n\
  445.   if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then\n\
  446.     shar_n= shar_c='\n\
  447. '\n\
  448.   else\n\
  449.     shar_n=-n shar_c=\n\
  450.   fi\n\
  451. else\n\
  452.   shar_n= shar_c='\\c'\n\
  453. fi\n",
  454.            output);
  455.       }
  456.  
  457.   if (timestamp_mode)
  458.     {
  459.       const char *file = "$$.touch";
  460.       const char *stamp = "1231235999";
  461.  
  462.       /* Set the shell variable shar_touch to `touch' if the touch
  463.      program is proven able to restore dates.  Otherwise, set
  464.      shar_touch to `:' and issue a warning.  */
  465.  
  466.       fprintf (output, "\
  467. touch -am %s %s >/dev/null 2>&1\n\
  468. if test ! -f %s && test -f %s; then\n\
  469.   shar_touch=touch\n\
  470. else\n\
  471.   shar_touch=:\n\
  472.   echo\n\
  473.   echo 'WARNING: not restoring timestamps.  Consider getting and'\n\
  474.   echo \"installing GNU \\`touch', distributed in GNU File Utilities...\"\n\
  475.   echo\n\
  476. fi\n\
  477. rm -f %s %s\n\
  478. #\n",
  479.            stamp, file, stamp, file, stamp, file);
  480.     }
  481. }
  482.  
  483. /*---.
  484. | ?  |
  485. `---*/
  486.  
  487. /* Ridiculously enough.  */
  488. #define MAX_MKDIR_ALREADY    128
  489.  
  490. char *mkdir_already[MAX_MKDIR_ALREADY];
  491. int mkdir_already_count = 0;
  492.  
  493. static void
  494. generate_mkdir (const char *path)
  495. {
  496.   int counter;
  497.   char *cursor;
  498.  
  499.   /* If already generated code for this dir creation, don't do again.  */
  500.  
  501.   for (counter = 0; counter < mkdir_already_count; counter++)
  502.     if (!strcmp (path, mkdir_already[counter]))
  503.       return;
  504.  
  505.   /* Haven't done this one.  */
  506.  
  507.   if (mkdir_already_count == MAX_MKDIR_ALREADY)
  508.     error (EXIT_FAILURE, 0, "Too many directories for mkdir generation");
  509.   cursor = mkdir_already[mkdir_already_count++] = xmalloc (strlen (path) + 1);
  510.   strcpy (cursor, path);
  511.  
  512.   /* Generate the text.  */
  513.  
  514.   fprintf (output, "if test ! -d '%s'; then\n", path);
  515.   if (!quiet_unshar_mode)
  516.     fprintf (output, "  echo 'x - creating directory %s'\n", path);
  517.   fprintf (output, "  mkdir '%s'\n", path);
  518.   fputs ("fi\n", output);
  519. }
  520.  
  521. /*---.
  522. | ?  |
  523. `---*/
  524.  
  525. static void
  526. generate_mkdir_script (const char *path)
  527. {
  528.   char *cursor;
  529.  
  530.   for (cursor = strchr (path, '/'); cursor; cursor = strchr (cursor + 1, '/'))
  531.     {
  532.  
  533.       /* Avoid empty string if leading or double '/'.  */
  534.  
  535.       if (cursor == path || *(cursor - 1) == '/')
  536.     continue;
  537.  
  538.       /* Omit '.'.  */
  539.  
  540.       if (cursor[-1] == '.' && (cursor == path + 1 || cursor[-2] == '/'))
  541.     continue;
  542.  
  543.       /* Temporarily terminate string.  FIXME!  */
  544.  
  545.       *cursor = 0;
  546.       generate_mkdir (path);
  547.       *cursor = '/';
  548.     }
  549. }
  550.  
  551. /* Walking routines.  */
  552.  
  553. /*---.
  554. | ?  |
  555. `---*/
  556.  
  557. static int
  558. check_accessibility (const char *local_name, const char *restore_name)
  559. {
  560.   if (access (local_name, 4))
  561.     {
  562.       error (0, 0, "Cannot access %s", local_name);
  563.       return 1;
  564.     }
  565.  
  566.   return 0;
  567. }
  568.  
  569. /*---.
  570. | ?  |
  571. `---*/
  572.  
  573. static int
  574. generate_one_header_line (const char *local_name, const char *restore_name)
  575. {
  576.   fprintf (output, "# %6ld %s %s\n", struct_stat.st_size,
  577.        mode_string (struct_stat.st_mode), restore_name);
  578.   return 0;
  579. }
  580.  
  581. /*---.
  582. | ?  |
  583. `---*/
  584.  
  585. static void
  586. generate_full_header (int argc, char *const *argv)
  587. {
  588.   char *current_directory;
  589.   time_t now;
  590.   struct tm *local_time;
  591.   char buffer[80];
  592.   int warned_once;
  593.   int counter;
  594.  
  595.   warned_once = 0;
  596.   for (counter = 0; counter < argc; counter++)
  597.     {
  598.  
  599.       /* Skip positional parameters.  */
  600.  
  601.       if (intermixed_parameter_mode &&
  602.       (strcmp (argv[counter], "-B") == 0 ||
  603.        strcmp (argv[counter], "-T") == 0 ||
  604.        strcmp (argv[counter], "-M") == 0 ||
  605.        strcmp (argv[counter], "-z") == 0 ||
  606.        strcmp (argv[counter], "-Z") == 0 ||
  607.        strcmp (argv[counter], "-C") == 0))
  608.     {
  609.       if (!warned_once && strcmp (argv[counter], "-C") == 0)
  610.         {
  611.           error (0, 0, "-C is being deprecated, use -Z instead");
  612.           warned_once = 1;
  613.         }
  614.       continue;
  615.     }
  616.  
  617.       if (walktree (check_accessibility, argv[counter]))
  618.     exit (EXIT_FAILURE);
  619.     }
  620.  
  621.   if (net_headers_mode)
  622.     {
  623.       fprintf (output, "Submitted-by: %s\n", submitter_address);
  624.       fprintf (output, "Archive-name: %s%s%02d\n\n",
  625.            archive_name, (strchr (archive_name, '/')) ? "" : "/part",
  626.            part_number ? part_number : 1);
  627.     }
  628.  
  629.   if (cut_mark_mode)
  630.     fputs (cut_mark_line, output);
  631.   fputs ("\
  632. #!/bin/sh\n",
  633.      output);
  634.   if (archive_name)
  635.     fprintf (output, "\
  636. # This is %s, a shell archive (produced by GNU %s %s)\n",
  637.          archive_name, PRODUCT, VERSION);
  638.   else
  639.     fprintf (output, "\
  640. # This is a shell archive (produced by GNU %s %s).\n",
  641.          PRODUCT, VERSION);
  642.   fputs ("\
  643. # To extract the files from this archive, save it to some FILE, remove\n\
  644. # everything before the `!/bin/sh' line above, then type `sh FILE'.\n\
  645. #\n",
  646.      output);
  647.  
  648.   time (&now);
  649.   local_time = localtime (&now);
  650.   strftime (buffer, 79, "%Y-%m-%d %H:%M %Z", local_time);
  651.   fprintf (output, "\
  652. # Made on %s by <%s>.\n",
  653.        buffer, submitter_address);
  654.  
  655.   current_directory = xgetcwd ();
  656.   if (current_directory)
  657.     {
  658.       fprintf (output, "\
  659. # Source directory was `%s'.\n",
  660.            current_directory);
  661.       free (current_directory);
  662.     }
  663.   else
  664.     error (0, errno, "Cannot get current directory name");
  665.  
  666.   if (check_existing_mode)
  667.     fputs ("\
  668. #\n\
  669. # Existing files will *not* be overwritten unless `-c' is specified.\n",
  670.        output);
  671.   else if (query_user_mode)
  672.     fputs ("\
  673. #\n\
  674. # existing files MAY be overwritten\n",
  675.        output);
  676.   else
  677.     fputs ("\
  678. #\n\
  679. # existing files WILL be overwritten\n",
  680.        output);
  681.  
  682.   if (query_user_mode)
  683.     fputs ("\
  684. # The unsharer will be INTERACTIVELY queried.\n",
  685.        output);
  686.  
  687.   if (vanilla_operation_mode)
  688.     {
  689.       fputs ("\
  690. # This format requires very little intelligence at unshar time.\n\
  691. # ",
  692.          output);
  693.       if (check_existing_mode || split_file_mode)
  694.     fputs ("\"if test\", ", output);
  695.       if (split_file_mode)
  696.     fputs ("\"cat\", \"rm\", ", output);
  697.       fputs ("\"echo\", and \"sed\" may be needed.\n", output);
  698.     }
  699.  
  700.   if (split_file_mode)
  701.     {
  702.  
  703.       /* May be split, explain.  */
  704.  
  705.       fputs ("#\n", output);
  706.       archive_type_position = ftell (output);
  707.       fprintf (output, "%-75s\n%-75s\n", "#", "#");
  708.     }
  709.  
  710.   fputs ("\
  711. #\n\
  712. # This shar contains:\n\
  713. # length mode       name\n\
  714. # ------ ---------- ------------------------------------------\n",
  715.      output);
  716.  
  717.   for (counter = 0; counter < argc; counter++)
  718.     {
  719.  
  720.       /* Output names of files but not parameters.  */
  721.  
  722.       if (intermixed_parameter_mode &&
  723.       (strcmp (argv[counter], "-B") == 0 ||
  724.        strcmp (argv[counter], "-T") == 0 ||
  725.        strcmp (argv[counter], "-M") == 0 ||
  726.        strcmp (argv[counter], "-z") == 0 ||
  727.        strcmp (argv[counter], "-Z") == 0 ||
  728.        strcmp (argv[counter], "-C") == 0))
  729.     continue;
  730.  
  731.       if (walktree (generate_one_header_line, argv[counter]))
  732.     exit (EXIT_FAILURE);
  733.     }
  734.   fputs ("#\n", output);
  735.  
  736.   generate_configure ();
  737.  
  738.   if (split_file_mode)
  739.     {
  740.  
  741.       /* Now check the sequence.  */
  742.  
  743.       fputs ("\
  744. if test -r _sharseq.tmp; then\n\
  745.   echo 'Must unpack archives in sequence!'\n\
  746.   echo Please unpack part `cat _sharseq.tmp` next\n\
  747.   exit 1\n\
  748. fi\n",
  749.          output);
  750.     }
  751. }
  752.  
  753. /* Prepare a shar script.  */
  754.  
  755. /*---.
  756. | ?  |
  757. `---*/
  758.  
  759. static int
  760. shar (const char *local_name, const char *restore_name)
  761. {
  762.   char buffer[BUFSIZ];
  763.   FILE *input;
  764.   long current_size;
  765.   long remaining_size;
  766.   int split_flag = 0;        /* file split flag */
  767.   const char *file_type;    /* text or binary */
  768.   struct tm *restore_time;
  769.  
  770.   /* Check to see that this is still a regular file and readable.  */
  771.  
  772.   if (!S_ISREG (struct_stat.st_mode & S_IFMT))
  773.     {
  774.       error (0, 0, "%s: Not a regular file", local_name);
  775.       return 1;
  776.     }
  777.   if (access (local_name, 4))
  778.     {
  779.       error (0, 0, "Cannot access %s", local_name);
  780.       return 1;
  781.     }
  782.  
  783.   /* If file_size_limit set, get the current output length.  */
  784.  
  785.   if (file_size_limit)
  786.     {
  787.       current_size = ftell (output);
  788.       remaining_size = (file_size_limit * 1024L) - current_size;
  789.       DEBUG_PRINT ("In shar: remaining size %ld\n", remaining_size);
  790.  
  791.       if (!split_file_mode && current_size > first_file_position
  792.       && ((uuencoded_file_mode
  793.            ? struct_stat.st_size + struct_stat.st_size / 3
  794.            : struct_stat.st_size)
  795.           > remaining_size))
  796.     {
  797.  
  798.       /* Change to another file.  */
  799.  
  800.       DEBUG_PRINT ("Newfile, remaining %ld, ", remaining_size);
  801.       DEBUG_PRINT ("limit still %d\n", file_size_limit);
  802.  
  803.       /* Close the "&&" and report an error if any of the above
  804.          failed.  */
  805.  
  806.       fprintf (output, "\
  807. : || echo 'restore of %s failed'\n\
  808. echo 'End of part %d, continue with part %d'\n\
  809. exit 0\n",
  810.            restore_name, part_number, part_number + 1);
  811.  
  812.       fclose (output);
  813.  
  814.       /* Clear mkdir_already in case the user unshars out of order.  */
  815.  
  816.       while (mkdir_already_count > 0)
  817.         free (mkdir_already[--mkdir_already_count]);
  818.  
  819.       /* Form the next filename.  */
  820.  
  821.       sprintf (output_filename, "%s%02d", output_base_name, ++part_number);
  822.       output = fopen (output_filename, "w");
  823.       if (!quiet_mode)
  824.         fprintf (stderr, "Starting file %s\n", output_filename);
  825.  
  826.       if (net_headers_mode)
  827.         {
  828.           fprintf (output, "Submitted-by: %s\n", submitter_address);
  829.           fprintf (output, "Archive-name: %s%s%02d\n\n", archive_name,
  830.                strchr (archive_name, '/') ? "" : "/part",
  831.                part_number ? part_number : 1);
  832.         }
  833.  
  834.       if (cut_mark_mode)
  835.         fputs (cut_mark_line, output);
  836.  
  837.       fprintf (output, "\
  838. #!/bin/sh\n\
  839. # This is part %02d of %s.\n",
  840.            part_number,
  841.            archive_name ? archive_name : "a multipart archive");
  842.  
  843.       generate_configure ();
  844.  
  845.       first_file_position = ftell (output);
  846.     }
  847.     }
  848.   else
  849.     remaining_size = 0;        /* give some value to the variable */
  850.  
  851.   fprintf (output, "\
  852. # ============= %s ==============\n",
  853.        restore_name);
  854.  
  855.   generate_mkdir_script (restore_name);
  856.  
  857.   if (struct_stat.st_size == 0)
  858.     {
  859.       file_type = "empty";
  860.       input = NULL;        /* give some value to the variable */
  861.     }
  862.   else
  863.     {
  864.  
  865.       /* If mixed, determine the file type.  */
  866.  
  867.       if (mixed_uuencoded_file_mode) 
  868.     {
  869.  
  870.       /* Uuencoded was once decided through calling the `file'
  871.          program and studying its output: the method was slow and
  872.          error prone.  There is only one way of doing it correctly,
  873.          and this is to read the input file, seeking for one binary
  874.          character.  Considering the average file size, even reading
  875.          the whole file (if it is text) would be usually faster than
  876.          calling `file'.  */
  877.  
  878.       int character;
  879.       int line_length;
  880.  
  881.       if (input = fopen (local_name, "rb"), input == NULL)
  882.         {
  883.           error (0, errno, "Cannot open file %s", local_name);
  884.           return 1;
  885.         }
  886.  
  887.       /* Assume initially that the input file is text.  Then try to prove
  888.          it is binary by looking for binary characters or long lines.  */
  889.  
  890.       uuencoded_file_mode = 0;
  891.       line_length = 0;
  892.       while (character = getc (input), character != EOF)
  893.         if (character == '\n')
  894.           line_length = 0;
  895.         else if (
  896. #ifdef __CHAR_UNSIGNED__
  897.              byte_is_binary[character]
  898. #else
  899.              byte_is_binary[character & 0xFF]
  900. #endif
  901.              || line_length == MAXIMUM_NON_BINARY_LINE)
  902.           {
  903.         uuencoded_file_mode = 1;
  904.         break;
  905.           }
  906.         else
  907.           line_length++;
  908.       fclose (input);
  909.  
  910.       /* Text files should terminate by an end of line.  */
  911.  
  912.       if (line_length > 0)
  913.         uuencoded_file_mode = 1;
  914.     }
  915.  
  916.       if (uuencoded_file_mode) 
  917.     {
  918.       static int pid, pipex[2];
  919.  
  920.       file_type = (compressed_file_mode ? "compressed"
  921.                : gzipped_file_mode ? "gzipped" : "binary");
  922.  
  923.       /* Fork a uuencode process.  */
  924.  
  925.       pipe (pipex);
  926.       fflush (output);
  927.  
  928.       if (pid = fork (), pid != 0)
  929.         {
  930.  
  931.           /* Parent, create a file to read.  */
  932.  
  933.           if (pid < 0)
  934.         error (EXIT_FAILURE, errno, "Could not fork");
  935.           close (pipex[1]);
  936.           input = fdopen (pipex[0], "r");
  937.           if (!input)
  938.         {
  939.           error (0, errno, "File %s (%s)", local_name, file_type);
  940.           return 1;
  941.         }
  942.         }
  943.       else
  944.         {
  945.  
  946.           /* Start writing the pipe with encodes.  */
  947.  
  948.           FILE *outptr;
  949.  
  950.           if (compressed_file_mode)
  951.         {
  952.           sprintf (buffer, "compress -b%d < '%s'",
  953.                bits_per_compressed_byte, local_name);
  954.           input = popen (buffer, "r");
  955.         }
  956.           else if (gzipped_file_mode)
  957.         {
  958.           sprintf (buffer, "gzip -%d < '%s'",
  959.                gzip_compression_level, local_name);
  960.           input = popen (buffer, "r");
  961.         }
  962.           else
  963.         input = fopen (local_name, "rb");
  964.  
  965.           outptr = fdopen (pipex[1], "w");
  966.           fprintf (outptr, "begin 600 %s\n",
  967.                (compressed_file_mode ? "_sharcmp.tmp"
  968.             : gzipped_file_mode ? "_shargzi.tmp" : restore_name));
  969.           copy_file_encoded (input, outptr);
  970.           fprintf (outptr, "end\n");
  971.           if (compressed_file_mode || gzipped_file_mode)
  972.         pclose (input);
  973.           else
  974.         fclose (input);
  975.  
  976.           exit (EXIT_SUCCESS);
  977.         }
  978.     }
  979.       else
  980.     {
  981.       file_type = "text";
  982.  
  983.       input = fopen (local_name, "r");
  984.       if (!input)
  985.         {
  986.           error (0, errno, "File %s (%s)", local_name, file_type);
  987.           return 1;
  988.         }
  989.     }
  990.     }
  991.  
  992.   /* Protect existing files.  */
  993.  
  994.   if (check_existing_mode)
  995.     {
  996.       fprintf (output, "\
  997. if test -f '%s' && test X\"$1\" != X\"-c\"; then\n",
  998.            restore_name);
  999.       
  1000.       if (query_user_mode)
  1001.     {
  1002.       fprintf (output, "\
  1003.   case $shar_wish in\n\
  1004.     A*|a*)\n\
  1005.       echo 'x - overwriting %s' ;;\n\
  1006.     *)\n\
  1007.       echo $shar_n \"? - overwrite %s [no, yes, all, quit] (no)? $shar_c\"\n\
  1008.       if test -n \"$shar_tty\"; then\n\
  1009.     read shar_wish < $shar_tty\n\
  1010.       else\n\
  1011.     read shar_wish\n\
  1012.       fi ;;\n\
  1013.   esac\n\
  1014.   case $shar_wish in\n\
  1015.     Q*|q*)\n\
  1016.       echo 'extraction aborted'; exit 1 ;;\n\
  1017.     A*|a*|Y*|y*)\n\
  1018.       shar_skip=no ;;\n\
  1019.     *)\n\
  1020.       shar_skip=yes ;;\n\
  1021.   esac\n\
  1022. else\n\
  1023.   shar_skip=no\n\
  1024. fi\n\
  1025. if test $shar_skip = yes; then\n\
  1026.   echo 'x - skipping %s'\n",
  1027.            restore_name, restore_name, restore_name);
  1028.     }
  1029.       else
  1030.     fprintf (output, "\
  1031.   echo 'x - skipping %s (file already exists)'\n",
  1032.          restore_name);
  1033.  
  1034.       if (split_file_mode)
  1035.     fputs ("\
  1036.   rm -f _sharnew.tmp\n",
  1037.            output);
  1038.  
  1039.       fputs ("\
  1040. else\n",
  1041.          output);
  1042.  
  1043.       if (split_file_mode)
  1044.     fputs ("\
  1045.   > _sharnew.tmp\n",
  1046.            output);
  1047.     }
  1048.  
  1049.   error (0, 0, "Saving %s (%s)", local_name, file_type);
  1050.       
  1051.   if (!quiet_unshar_mode)
  1052.     fprintf (output, "\
  1053.   echo 'x - extracting %s (%s)'\n",
  1054.          restore_name, file_type);
  1055.  
  1056.   if (struct_stat.st_size == 0)
  1057.     {
  1058.  
  1059.       /* Just touch the file, or empty it if it exists.  */
  1060.  
  1061.       fprintf (output, "\
  1062.   > '%s' &&\n",
  1063.            restore_name);
  1064.     }
  1065.   else
  1066.     {
  1067.  
  1068.       /* Run sed for non-empty files.  */
  1069.  
  1070.       if (uuencoded_file_mode)
  1071.     {
  1072.  
  1073.       /* Run sed through uudecode (via temp file if might get split).  */
  1074.  
  1075.       fprintf (output, "\
  1076.   sed 's/^%c//' << '%s' %s &&\n",
  1077.            line_prefix, here_delimiter,
  1078.            inhibit_piping_mode ? "> _sharuue.tmp" : "| uudecode");
  1079.     }
  1080.       else
  1081.     {
  1082.  
  1083.       /* Just run it into the file.  */
  1084.  
  1085.       fprintf (output, "\
  1086.   sed 's/^%c//' << '%s' > '%s' &&\n",
  1087.            line_prefix, here_delimiter, restore_name);
  1088.     }
  1089.  
  1090.       while (fgets (buffer, BUFSIZ, input))
  1091.     {
  1092.  
  1093.       /* Output a line and test the length.  */
  1094.  
  1095.       if (!mandatory_prefix_mode
  1096.  
  1097. #ifdef isgraph
  1098.           && isgraph (buffer[0])
  1099. #else
  1100.           && isprint (buffer[0]) && !isspace (buffer[0])
  1101. #endif
  1102.  
  1103.           /* Protect lines already starting with the prefix.  */
  1104.           && buffer[0] != line_prefix
  1105.  
  1106.           /* Old mail programs interpret ~ directives.  */
  1107.           && buffer[0] != '~'
  1108.  
  1109.           /* Avoid mailing lines which are just `.'.  */
  1110.           && buffer[0] != '.'
  1111.  
  1112. #if STRNCMP_IS_FAST
  1113.           && strncmp (buffer, here_delimiter, here_delimiter_length)
  1114.  
  1115.           /* unshar -e: avoid `exit 0'.  */
  1116.           && strncmp (buffer, "exit 0", 6)
  1117.  
  1118.           /* Don't let mail prepend a `>'.  */
  1119.           && strncmp (buffer, "From", 4)
  1120. #else
  1121.           && (buffer[0] != here_delimiter[0]
  1122.           || strncmp (buffer, here_delimiter, here_delimiter_length))
  1123.  
  1124.           /* unshar -e: avoid `exit 0'.  */
  1125.           && (buffer[0] != 'e' || strncmp (buffer, "exit 0", 6))
  1126.  
  1127.           /* Don't let mail prepend a `>'.  */
  1128.           && (buffer[0] != 'F' || strncmp (buffer, "From", 4))
  1129. #endif
  1130.           )
  1131.         fputs (buffer, output);
  1132.       else
  1133.         {
  1134.           fprintf (output, "%c%s", line_prefix, buffer);
  1135.           remaining_size--;
  1136.         }
  1137.  
  1138.       /* Try completing an incomplete line, but not if the incomplete
  1139.          line contains no character.  This might occur with -T for
  1140.          incomplete files, or sometimes when switching to a new file.  */
  1141.  
  1142.       if (*buffer && buffer[strlen (buffer) - 1] != '\n')
  1143.         {
  1144.           fputc ('\n', output);
  1145.           remaining_size--;
  1146.         }
  1147.  
  1148.       if (split_file_mode
  1149. #if MSDOS
  1150.           /* 1 extra for CR.  */
  1151.           && (remaining_size -= (int) strlen (buffer) + 1) < 0
  1152. #else
  1153.           && (remaining_size -= (int) strlen (buffer)) < 0
  1154. #endif
  1155.           )
  1156.         {
  1157.  
  1158.           /* Change to another file.  */
  1159.  
  1160.           DEBUG_PRINT ("Newfile, remaining %ld, ", remaining_size);
  1161.           DEBUG_PRINT ("limit still %d\n", file_size_limit);
  1162.  
  1163.           fprintf (output, "%s\n", here_delimiter);
  1164.  
  1165.           /* Close the "&&" and report an error if any of the above
  1166.          failed.  */
  1167.  
  1168.           fprintf (output, "\
  1169.   : || echo 'restore of %s failed'\n",
  1170.                restore_name);
  1171.  
  1172.           if (check_existing_mode)
  1173.         fputs ("\
  1174. fi\n",
  1175.                output);
  1176.  
  1177.           if (quiet_unshar_mode)
  1178.         fprintf (output, "\
  1179. echo 'End of part %d, continue with part %d'\n",
  1180.              part_number, part_number + 1);
  1181.           else
  1182.         fprintf (output, "\
  1183. echo 'End of %s part %d'\n\
  1184. echo 'File %s is continued in part %d'\n",
  1185.              archive_name ? archive_name : "archive",
  1186.              part_number, restore_name, part_number + 1);
  1187.  
  1188.           fprintf (output, "\
  1189. echo %d > _sharseq.tmp\n\
  1190. exit 0\n",
  1191.                part_number + 1);
  1192.  
  1193.           if (part_number == 1)
  1194.         {
  1195.  
  1196.           /* Rewrite the info lines on the first header.  */
  1197.  
  1198.           fseek (output, archive_type_position, 0);
  1199.           fprintf (output, "%-75s\n%-75s\n",
  1200.                "\
  1201. # This is part 1 of a multipart archive.",
  1202.                "\
  1203. # Do not concatenate these parts, unpack them in order with `/bin/sh'.");
  1204.                }
  1205.           fclose (output);
  1206.  
  1207.           /* Form the next filename.  */
  1208.  
  1209.           sprintf (output_filename, "%s%02d",
  1210.                output_base_name, ++part_number);
  1211.           output = fopen (output_filename, "w");
  1212.  
  1213.           if (net_headers_mode)
  1214.         {
  1215.           fprintf (output, "Submitted-by: %s\n", submitter_address);
  1216.           fprintf (output, "Archive-name: %s%s%02d\n\n",
  1217.                archive_name,
  1218.                strchr (archive_name, '/') ? "" : "/part",
  1219.                part_number ? part_number : 1);
  1220.         }
  1221.  
  1222.           if (cut_mark_mode)
  1223.         fputs (cut_mark_line, output);
  1224.  
  1225.           fprintf (output, "\
  1226. #!/bin/sh\n\
  1227. # This is `%s' (part %d of %s).\n\
  1228. # Do not concatenate these parts, unpack them in order with `/bin/sh'.\n\
  1229. # File `%s' is being continued...\n\
  1230. #\n",
  1231.                basename (output_filename), part_number,
  1232.                archive_name ? archive_name : "a multipart archive",
  1233.                restore_name);
  1234.  
  1235.           generate_configure ();
  1236.  
  1237.           fprintf (output, "\
  1238. if test ! -r _sharseq.tmp; then\n\
  1239.   echo 'Please unpack part 1 first!'\n\
  1240.   exit 1\n\
  1241. fi\n\
  1242. shar_sequence=`cat _sharseq.tmp`\n\
  1243. if test \"$shar_sequence\" != %d; then\n\
  1244.   echo \"Please unpack part $shar_sequence next!\"\n\
  1245.   exit 1\n\
  1246. fi\n",
  1247.                part_number);
  1248.  
  1249.           if (check_existing_mode)
  1250.         if (quiet_unshar_mode)
  1251.           fputs ("\
  1252. if test -f _sharnew.tmp; then\n",
  1253.              output);
  1254.         else
  1255.           fprintf (output, "\
  1256. if test ! -f _sharnew.tmp; then\n\
  1257.   echo 'x - still skipping %s'\n\
  1258. else\n",
  1259.                restore_name);
  1260.  
  1261.           if (!quiet_mode)
  1262.         fprintf (stderr, "Starting file %s\n", output_filename);
  1263.           if (!quiet_unshar_mode)
  1264.         fprintf (output, "\
  1265.   echo 'x - continuing file %s'\n",
  1266.              restore_name);
  1267.           fprintf (output, "\
  1268.   sed 's/^%c//' << '%s' >> '%s' &&\n",
  1269.                line_prefix, here_delimiter,
  1270.                uuencoded_file_mode ? "_sharuue.tmp" : restore_name);
  1271.           remaining_size = file_size_limit * 1024L;
  1272.           split_flag = 1;
  1273.         }
  1274.     }
  1275.  
  1276.       fclose (input);
  1277.       while (wait (NULL) >= 0)
  1278.     ;
  1279.  
  1280.       fprintf (output, "%s\n", here_delimiter);
  1281.       if (split_flag && !quiet_unshar_mode)
  1282.     fprintf (output, "\
  1283.   echo 'File %s is complete' &&\n", restore_name);
  1284.  
  1285.       /* If this file was uuencoded w/Split, decode it and drop the temp.  */
  1286.  
  1287.       if (uuencoded_file_mode && inhibit_piping_mode)
  1288.     {
  1289.       if (!quiet_unshar_mode)
  1290.         fprintf (output, "\
  1291.   echo 'uudecoding file %s' &&\n",
  1292.              restore_name);
  1293.  
  1294.       fputs ("\
  1295.   uudecode _sharuue.tmp < _sharuue.tmp && rm -f _sharuue.tmp &&\n",
  1296.          output);
  1297.     }
  1298.  
  1299.       /* If this file was compressed, uncompress it and drop the temp.  */
  1300.  
  1301.       if (compressed_file_mode)
  1302.     {
  1303.       if (!quiet_unshar_mode)
  1304.         fprintf (output, "\
  1305.   echo 'uncompressing file %s' &&\n",
  1306.              restore_name);
  1307.  
  1308.       fprintf (output, "\
  1309.   compress -d < _sharcmp.tmp > '%s' && rm -f _sharcmp.tmp &&\n",
  1310.            restore_name);
  1311.     }
  1312.       else if (gzipped_file_mode)
  1313.     {
  1314.       if (!quiet_unshar_mode)
  1315.         fprintf (output, "\
  1316.   echo 'gunzipping file %s' &&\n",
  1317.              restore_name);
  1318.  
  1319.       fprintf (output, "\
  1320.   gzip -d < _shargzi.tmp > '%s' && rm -f _shargzi.tmp &&\n",
  1321.            restore_name);
  1322.     }
  1323.     }
  1324.  
  1325.   if (timestamp_mode)
  1326.     {
  1327.  
  1328.       /* Set the dates as they were.  */
  1329.  
  1330.       restore_time = localtime (&struct_stat.st_mtime);
  1331.       fprintf (output, "\
  1332.   $shar_touch -am %02d%02d%02d%02d%02d '%s' &&\n",
  1333.            restore_time->tm_mon + 1, restore_time->tm_mday,
  1334.            restore_time->tm_hour, restore_time->tm_min,
  1335.            restore_time->tm_year, restore_name);
  1336.     }
  1337.  
  1338.   if (vanilla_operation_mode)
  1339.     {
  1340.  
  1341.       /* Close the "&&" and report an error if any of the above
  1342.      failed.  */
  1343.  
  1344.       fprintf (output, "\
  1345.   : || echo 'restore of %s failed'\n",
  1346.            restore_name);
  1347.     }
  1348.   else
  1349.     {
  1350.  
  1351.       /* Set the permissions as they were.  */
  1352.  
  1353.       fprintf (output, "\
  1354.   chmod %04o '%s' ||\n",
  1355.            (unsigned) (struct_stat.st_mode & 0777), restore_name);
  1356.  
  1357.       /* Report an error if any of the above failed.  */
  1358.  
  1359.       fprintf (output, "\
  1360.   echo 'restore of %s failed'\n",
  1361.            restore_name);
  1362.  
  1363.       if (character_count_mode)
  1364.     {
  1365.  
  1366.       /* Validate the transferred file.  */
  1367.  
  1368.       FILE *pfp;
  1369.       char command[BUFSIZ];
  1370.  
  1371.       sprintf (command, "%s '%s'", CHARACTER_COUNT_COMMAND, local_name);
  1372.       if (pfp = popen (command, "r"), pfp)
  1373.         {
  1374.           char wc[BUFSIZ];
  1375.  
  1376.           fscanf (pfp, "%s", wc);
  1377.           fprintf (output, "\
  1378.   shar_count=\"`%s '%s'`\"\n\
  1379.   test %s -eq \"$shar_count\" ||\n\
  1380.     echo \"%s: original size %s, current size $shar_count\"\n",
  1381.                CHARACTER_COUNT_COMMAND, restore_name,
  1382.                wc, restore_name, wc);
  1383.           pclose (pfp);
  1384.         }
  1385.     }
  1386.     }
  1387.  
  1388.   /* If the exists option is in place close the if.  */
  1389.  
  1390.   if (check_existing_mode)
  1391.     {
  1392.       if (split_file_mode)
  1393.     fputs ("\
  1394.   rm -f _sharnew.tmp\n",
  1395.            output);
  1396.  
  1397.       fputs ("\
  1398. fi\n",
  1399.          output);
  1400.     }
  1401.  
  1402.   return 0;
  1403. }
  1404.  
  1405. /* Main control.  */
  1406.  
  1407. /*-----------------------------------------------------------------------.
  1408. | Set file mode, accepting a parameter 'M' for mixed uuencoded mode, 'B' |
  1409. | for uuencoded mode, 'z' for gzipped mode or 'Z' for compressed mode.     |
  1410. | Any other value yields text mode.                     |
  1411. `-----------------------------------------------------------------------*/
  1412.  
  1413. static void
  1414. set_file_mode (int mode)
  1415. {
  1416.   mixed_uuencoded_file_mode = mode == 'M';
  1417.   uuencoded_file_mode = mode == 'B';
  1418.   gzipped_file_mode = mode == 'z';
  1419.   compressed_file_mode = mode == 'Z';
  1420.  
  1421.   if (gzipped_file_mode || compressed_file_mode)
  1422.     uuencoded_file_mode = 1;
  1423. }
  1424.  
  1425. /*----------------------------------.
  1426. | Output a command format message.  |
  1427. `----------------------------------*/
  1428.  
  1429. static void
  1430. usage (int status)
  1431. {
  1432.   if (status != EXIT_SUCCESS)
  1433.     fprintf (stderr, "Try `%s --help' for more information.\n", program_name);
  1434.   else
  1435.     {
  1436.       printf ("Usage: %s [OPTION]... [FILE]...\n", program_name);
  1437.       fputs ("\
  1438. Mandatory arguments to long options are mandatory for short options too.\n",
  1439.          stdout);
  1440.       fputs ("\
  1441. \n\
  1442. Giving feedback:\n\
  1443.       --help              display this help and exit\n\
  1444.       --version           output version information and exit\n\
  1445.   -q, --quiet, --silent   do not output verbose messages locally\n\
  1446. \n\
  1447. Selecting files:\n\
  1448.   -p, --intermix-type     allow -[BTzZ] in file lists to change mode\n\
  1449.   -S, --stdin-file-list   read file list from standard input\n\
  1450. \n\
  1451. Splitting output:\n\
  1452.   -o, --output-prefix=PREFIX    output to file PREFIX.01 through PREFIX.NN\n\
  1453.   -l, --whole-size-limit=SIZE   split archive, not files, to SIZE kilobytes\n\
  1454.   -L, --split-size-limit=SIZE   split archive, or files, to SIZE kilobytes\n",
  1455.          stdout);
  1456.       fputs ("\
  1457. \n\
  1458. Controlling the shar headers:\n\
  1459.   -n, --archive-name=NAME   use NAME to document the archive\n\
  1460.   -s, --submitter=ADDRESS   override the submitter name\n\
  1461.   -a, --net-headers         output Submitted-by: & Archive-name: headers\n\
  1462.   -c, --cut-mark            start the shar with a cut line\n\
  1463. \n\
  1464. Selecting how files are stocked:\n\
  1465.   -M, --mixed-uuencode         dynamically decide uuencoding (default)\n\
  1466.   -T, --text-files             treat all files as text\n\
  1467.   -B, --uuencode               treat all files as binary, use uuencode\n\
  1468.   -z, --gzip                   gzip and uuencode all files\n\
  1469.   -g, --level-for-gzip=LEVEL   pass -LEVEL (default 9) to gzip\n\
  1470.   -Z, --compress               compress and uuencode all files\n\
  1471.   -b, --bits-per-code=BITS     pass -bBITS (default 12) to compress\n",
  1472.          stdout);
  1473.       fputs ("\
  1474. \n\
  1475. Protecting against transmission:\n\
  1476.   -w, --no-character-count      do not use `wc -c' to check size\n\
  1477.   -F, --force-prefix            force the prefix character on every line\n\
  1478.   -d, --here-delimiter=STRING   use STRING to delimit the files in the shar\n\
  1479. \n\
  1480. Producing different kinds of shars:\n\
  1481.   -V, --vanilla-operation   produce very simple and undemanding shars\n\
  1482.   -P, --no-piping           exclusively use temporary files at unshar time\n\
  1483.   -x, --no-check-existing   blindly overwrite existing files\n\
  1484.   -X, --query-user          ask user before overwriting files (not for Net)\n\
  1485.   -m, --no-timestamp        do not restore file modification dates & times\n\
  1486.   -Q, --quiet-unshar        avoid verbose messages at unshar time\n\
  1487.   -f, --basename            restore all in one directory, despite hierarchy\n",
  1488.          stdout);
  1489.       fputs ("\
  1490. \n\
  1491. Option -o is required with -l or -L, option -n is required with -a.\n\
  1492. Option -g implies -z, option -b implies -Z.\n",
  1493.          stdout);
  1494.     }
  1495.   exit (status);
  1496. }
  1497.  
  1498. /*--------------------------------------.
  1499. | Decode options and launch execution.  |
  1500. `--------------------------------------*/
  1501.  
  1502. static const struct option long_options[] =
  1503. {
  1504.   {"archive-name", required_argument, NULL, 'n'},
  1505.   {"basename", no_argument, NULL, 'f'}, 
  1506.   {"bits-per-code", required_argument, NULL, 'b'},
  1507.   {"compress", no_argument, NULL, 'Z'}, 
  1508.   {"cut-mark", no_argument, NULL, 'c'}, 
  1509.   {"force-prefix", no_argument, NULL, 'F'}, 
  1510.   {"gzip", no_argument, NULL, 'z'}, 
  1511.   {"here-delimiter", required_argument, NULL, 'd'},
  1512.   {"intermix-type", no_argument, NULL, 'p'}, 
  1513.   {"level-for-gzip", required_argument, NULL, 'g'},
  1514.   {"mixed-uuencode", no_argument, NULL, 'M'}, 
  1515.   {"net-headers", no_argument, NULL, 'a'}, 
  1516.   {"no-character-count", no_argument, NULL, 'w'}, 
  1517.   {"no-check-existing", no_argument, NULL, 'x'}, 
  1518.   {"no-piping", no_argument, NULL, 'P'}, 
  1519.   {"no-timestamp", no_argument, NULL, 'm'}, 
  1520.   {"output-prefix", required_argument, NULL, 'o'},
  1521.   {"query-user", no_argument, NULL, 'X'}, 
  1522.   {"quiet", no_argument, NULL, 'q'}, 
  1523.   {"quiet-unshar", no_argument, NULL, 'Q'}, 
  1524.   {"split-size-limit", required_argument, NULL, 'L'},
  1525.   {"stdin-file-list", no_argument, NULL, 'S'}, 
  1526.   {"submitter", required_argument, NULL, 's'},
  1527.   {"text-files", no_argument, NULL, 'T'}, 
  1528.   {"uuencode", no_argument, NULL, 'B'}, 
  1529.   {"vanilla-operation", no_argument, NULL, 'V'}, 
  1530.   {"whole-size-limit", required_argument, NULL, 'l'},
  1531.  
  1532.   {"help", no_argument, &show_help, 1},
  1533.   {"version", no_argument, &show_version, 1},
  1534.  
  1535.   { 0, 0, 0, 0 },
  1536. };
  1537.  
  1538. /*---.
  1539. | ?  |
  1540. `---*/
  1541.  
  1542. int
  1543. main (int argc, char *const *argv)
  1544. {
  1545.   int status = EXIT_SUCCESS;
  1546.   int stdin_file_list = 0;
  1547.   int optchar;
  1548.  
  1549.   program_name = argv[0];
  1550.  
  1551.   while (optchar = getopt_long (argc, argv,
  1552.                 "$BCFL:MPQSTVXZab:cd:fg:hl:mn:o:pqs:wxz",
  1553.                 long_options, NULL),
  1554.      optchar != EOF)
  1555.     switch (optchar)
  1556.       {
  1557.       case '\0':
  1558.     break;
  1559.  
  1560.       case '$':
  1561. #if DEBUG
  1562.     debugging_mode = 1;
  1563. #else
  1564.     error (0, 0, "DEBUG was not selected at compile time");
  1565. #endif
  1566.     break;
  1567.  
  1568.       case 'B':
  1569.     set_file_mode ('B');
  1570.     break;
  1571.  
  1572.       case 'F':
  1573.     mandatory_prefix_mode = 1;
  1574.     break;
  1575.  
  1576.       case 'L':
  1577.     if (file_size_limit = atoi (optarg), file_size_limit > 1)
  1578.       file_size_limit--;
  1579.     split_file_mode = file_size_limit != 0;
  1580.     inhibit_piping_mode = 1;
  1581.     DEBUG_PRINT ("Hard limit %dk\n", file_size_limit);
  1582.     break;
  1583.  
  1584.       case 'M':
  1585.     set_file_mode ('M');
  1586.     break;
  1587.  
  1588.       case 'P':
  1589.     inhibit_piping_mode = 1;
  1590.     break;
  1591.  
  1592.       case 'Q':
  1593.     quiet_unshar_mode = 1;
  1594.     break;
  1595.  
  1596.       case 'S':
  1597.     stdin_file_list = 1;
  1598.     break;
  1599.  
  1600.       case 'V':
  1601.     vanilla_operation_mode = 1;
  1602.     break;
  1603.  
  1604.       case 'T':
  1605.     set_file_mode ('T');
  1606.     break;
  1607.  
  1608.       case 'X':
  1609.     query_user_mode = 1;
  1610.     check_existing_mode = 1;
  1611.     break;
  1612.  
  1613.       case 'b':
  1614.     bits_per_compressed_byte = atoi (optarg);
  1615.     /* Fall through.  */
  1616.  
  1617.       case 'C':
  1618.       case 'Z':
  1619.     if (optchar == 'C')
  1620.       error (0, 0, "-C is being deprecated, use -Z instead");
  1621.     set_file_mode ('Z');
  1622.     break;
  1623.  
  1624.       case 'a':
  1625.     net_headers_mode = 1;
  1626.     break;
  1627.  
  1628.       case 'c':
  1629.     cut_mark_mode = 1;
  1630.     break;
  1631.  
  1632.       case 'd':
  1633.     here_delimiter = optarg;
  1634.     break;
  1635.  
  1636.       case 'f':
  1637.     basename_mode = 1;
  1638.     break;
  1639.  
  1640.       case 'h':
  1641.     usage (EXIT_SUCCESS);
  1642.     break;
  1643.  
  1644.       case 'l':
  1645.     if (file_size_limit = atoi (optarg), file_size_limit > 1)
  1646.       file_size_limit--;
  1647.     split_file_mode = 0;
  1648.     DEBUG_PRINT ("Soft limit %dk\n", file_size_limit);
  1649.     break;
  1650.  
  1651.       case 'm':
  1652.     timestamp_mode = 0;
  1653.     break;
  1654.  
  1655.       case 'n':
  1656.     archive_name = optarg;
  1657.     break;
  1658.  
  1659.       case 'o':
  1660.     strcpy (output_base_name, optarg);
  1661.     strcat (output_base_name, ".");
  1662.     part_number = 1;
  1663.     strcpy (output_filename, output_base_name);
  1664.     strcat (output_filename, "01");
  1665.     output = fopen (output_filename, "w");
  1666.     if (!output)
  1667.       error (EXIT_FAILURE, errno, output_filename);
  1668.     break;
  1669.  
  1670.       case 'p':
  1671.     intermixed_parameter_mode = 1;
  1672.     break;
  1673.  
  1674.       case 'q':
  1675.     quiet_mode = 1;
  1676.     break;
  1677.  
  1678.       case 's':
  1679.     submitter_address = optarg;
  1680.     break;
  1681.  
  1682.       case 'w':
  1683.     character_count_mode = 0;
  1684.     break;
  1685.  
  1686.       case 'x':
  1687.     check_existing_mode = 0;
  1688.     break;
  1689.  
  1690.       case 'g':
  1691.     gzip_compression_level = atoi (optarg);
  1692.     /* Fall through.  */
  1693.  
  1694.       case 'z':
  1695.     set_file_mode ('z');
  1696.     break;
  1697.  
  1698.       default:
  1699.     usage (EXIT_FAILURE);
  1700.       }
  1701.  
  1702.   if (show_version)
  1703.     {
  1704.       printf ("GNU %s %s\n", PRODUCT, VERSION);
  1705.       exit (EXIT_SUCCESS);
  1706.     }
  1707.  
  1708.   if (show_help)
  1709.     usage (EXIT_SUCCESS);
  1710.  
  1711.   line_prefix = (here_delimiter[0] == DEFAULT_LINE_PREFIX_1
  1712.          ? DEFAULT_LINE_PREFIX_2
  1713.          : DEFAULT_LINE_PREFIX_1);
  1714.  
  1715.   here_delimiter_length = strlen (here_delimiter);
  1716.  
  1717.   if (vanilla_operation_mode)
  1718.     {
  1719.       if (mixed_uuencoded_file_mode < 0)
  1720.     set_file_mode ('T');
  1721.  
  1722.       /* Implies -m, -w, -F and -P.  */
  1723.  
  1724.       timestamp_mode = 0;
  1725.       character_count_mode = 0;
  1726.       mandatory_prefix_mode = 1;
  1727.       inhibit_piping_mode = 1;
  1728.  
  1729.       /* Forbids -X.  */
  1730.  
  1731.       if (query_user_mode)
  1732.     {
  1733.       error (0, 0, "WARNING: No user interaction in vanilla mode");
  1734.       query_user_mode = 0;
  1735.     }
  1736.  
  1737.       /* Diagnose if not in -T state.  */
  1738.  
  1739.       if (mixed_uuencoded_file_mode
  1740.       || uuencoded_file_mode
  1741.       || gzipped_file_mode
  1742.       || compressed_file_mode
  1743.       || intermixed_parameter_mode)
  1744.     error (0, 0, "WARNING: Non-text storage options overridden");
  1745.     }
  1746.  
  1747.   /* Set defaults for unset options.  */
  1748.  
  1749.   if (mixed_uuencoded_file_mode < 0)
  1750.     set_file_mode ('M');
  1751.  
  1752.   if (!submitter_address)
  1753.     {
  1754.       submitter_address = xmalloc (128);
  1755.       get_submitter (submitter_address);
  1756.     }
  1757.  
  1758.   if (!output)
  1759.     output = stdout;
  1760.  
  1761.   /* Maybe prepare to decide dynamically about file type.  */
  1762.  
  1763.   if (mixed_uuencoded_file_mode || intermixed_parameter_mode)
  1764.     {
  1765.       memset ((char *) byte_is_binary, 1, 256);
  1766.       byte_is_binary['\b'] = 0;
  1767.       byte_is_binary['\t'] = 0;
  1768.       byte_is_binary['\f'] = 0;
  1769.       memset ((char *) byte_is_binary + 32, 0, 127 - 32);
  1770.     }
  1771.  
  1772.   /* Maybe read file list from standard input.  */
  1773.  
  1774.   if (stdin_file_list)
  1775.     {
  1776.       char stdin_buf[258];
  1777.       char **list;
  1778.  
  1779.       argc = 0;
  1780.       list = (char **) xmalloc (1024 * sizeof (char *));
  1781.       stdin_buf[0] = 0;
  1782.       while (fgets (stdin_buf, sizeof (stdin_buf), stdin))
  1783.     {
  1784.       if (argc == 1024)
  1785.         error (EXIT_FAILURE, 0,
  1786.            "Cannot handle more than 1024 files from stdin");
  1787.       if (stdin_buf[0])
  1788.         stdin_buf[strlen (stdin_buf) - 1] = 0;
  1789.       list[argc] = xmalloc (strlen (stdin_buf) + 1);
  1790.       strcpy (list[argc], stdin_buf);
  1791.       argc++;
  1792.       stdin_buf[0] = 0;
  1793.     }
  1794.       argv = list;
  1795.       optind = 0;
  1796.     }
  1797.  
  1798.   /* Diagnose various usage errors.  */
  1799.  
  1800.   if (optind >= argc)
  1801.     {
  1802.       error (0, 0, "No input files");
  1803.       usage (EXIT_FAILURE);
  1804.     }
  1805.  
  1806.   if (net_headers_mode && !archive_name)
  1807.     {
  1808.       error (0, 0, "Cannot use -a option without -n");
  1809.       usage (EXIT_FAILURE);
  1810.     }
  1811.  
  1812.   if (file_size_limit && !part_number)
  1813.     {
  1814.       error (0, 0, "Cannot use -l or -L option without -o");
  1815.       usage (EXIT_FAILURE);
  1816.     }
  1817.  
  1818.   /* Start making the archive file.  */
  1819.  
  1820.   generate_full_header (argc - optind, &argv[optind]);
  1821.  
  1822.   if (query_user_mode)
  1823.     {
  1824.       quiet_unshar_mode = 0;
  1825.       if (net_headers_mode)
  1826.     error (0, 0, "PLEASE avoid -X shars on Usenet or public networks");
  1827.  
  1828.       fputs ("\
  1829. shar_wish=\n",
  1830.          output);
  1831.     }
  1832.  
  1833.   first_file_position = ftell (output);
  1834.  
  1835.   /* Process positional parameters and files.  */
  1836.  
  1837.   for (; optind < argc; optind++)
  1838.     if (intermixed_parameter_mode)
  1839.       if (strcmp (argv[optind], "-B") == 0)
  1840.     set_file_mode ('B');
  1841.       else if (strcmp (argv[optind], "-T") == 0)
  1842.     set_file_mode ('T');
  1843.       else if (strcmp (argv[optind], "-M") == 0)
  1844.     set_file_mode ('M');
  1845.       else if (strcmp (argv[optind], "-z") == 0)
  1846.     set_file_mode ('z');
  1847.       else if (strcmp (argv[optind], "-Z") == 0
  1848.            || strcmp (argv[optind], "-C") == 0)
  1849.     set_file_mode ('Z');
  1850.       else
  1851.     {
  1852.       if (walktree (shar, argv[optind]))
  1853.         status = EXIT_FAILURE;
  1854.     }
  1855.     else
  1856.       {
  1857.     if (walktree (shar, argv[optind]))
  1858.       status = EXIT_FAILURE;
  1859.       }
  1860.  
  1861.   /* Delete the sequence file, if any.  */
  1862.  
  1863.   if (split_file_mode && part_number > 1)
  1864.     {
  1865.       fputs ("\
  1866. rm -f _sharseq.tmp\n\
  1867. echo 'You have unpacked the last part'\n",
  1868.          output);
  1869.       if (!quiet_mode)
  1870.     fprintf (stderr, "Created %d files\n", part_number);
  1871.     }
  1872.  
  1873.   fputs ("\
  1874. exit 0\n",
  1875.      output);
  1876.  
  1877.   exit (status);
  1878. }
  1879.