home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / sharutils-4.1-src.tgz / tar.out / fsf / sharutils / unshar.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  11KB  |  428 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. /* Unpackage one or more shell archive files.  The `unshar' program is a
  20.    filter which removes the front part of a file and passes the rest to
  21.    the `sh' command.  It understands phrases like "cut here", and also
  22.    knows about shell comment characters and the Unix commands `echo',
  23.    `cat', and `sed'.  */
  24.  
  25. #include "system.h"
  26. #include "getopt.h"
  27.  
  28. /* Buffer size for holding a file name.  */
  29. #define NAME_BUFFER_SIZE 1024
  30.  
  31. /* Buffer size for shell process input.  */
  32. #define SHELL_BUFFER_SIZE 8196
  33.  
  34. #define EOL '\n'
  35.  
  36. /* The name this program was run with. */
  37. const char *program_name;
  38.  
  39. /* If non-zero, display usage information and exit.  */
  40. static int show_help = 0;
  41.  
  42. /* If non-zero, print the version on standard output and exit.  */
  43. static int show_version = 0;
  44.  
  45. static int pass_c_flag = 0;
  46. static int continue_reading = 0;
  47. static const char *exit_string = "exit 0";
  48. static size_t exit_string_length;
  49. static char *current_directory;
  50.  
  51. /*-------------------------------------------------------------------------.
  52. | Match the leftmost part of a string.  Returns 1 if initial characters of |
  53. | DATA match PATTERN exactly; else 0.                       |
  54. `-------------------------------------------------------------------------*/
  55.  
  56. static int 
  57. starting_with (const char *data, const char *pattern)
  58. {
  59.   const char *pattern_cursor, *data_cursor;
  60.  
  61.   pattern_cursor = pattern;
  62.   data_cursor = data;
  63.   do
  64.     {
  65.       if (*pattern_cursor == '\0')
  66.     return 1;
  67.     }
  68.   while (*pattern_cursor++ == *data_cursor++);
  69.  
  70.   return 0;
  71. }
  72.  
  73. /*-------------------------------------------------------------------------.
  74. | For a DATA string and a PATTERN containing one or more embedded       |
  75. | asterisks (matching any number of characters), return non-zero if the       |
  76. | match succeeds, and set RESULT_ARRAY[I] to the characters matched by the |
  77. | I'th *.                                   |
  78. `-------------------------------------------------------------------------*/
  79.  
  80. static int
  81. matched_by (const char *data, const char *pattern, char **result_array)
  82. {
  83.   const char *pattern_cursor = NULL;
  84.   const char *data_cursor = NULL;
  85.   char *result_cursor = NULL;
  86.   int number_of_results = 0;
  87.  
  88.   while (1)
  89.     if (*pattern == '*')
  90.       {
  91.     pattern_cursor = ++pattern;
  92.     data_cursor = data;
  93.     result_cursor = result_array[number_of_results++];
  94.     *result_cursor = '\0';
  95.       }
  96.     else if (*data == *pattern)
  97.       {
  98.     if (*pattern == '\0')
  99.       /* The pattern matches.  */
  100.       return 1;
  101.  
  102.     pattern++;
  103.     data++;
  104.       }
  105.     else
  106.       {
  107.     if (*data == '\0')
  108.       /* The pattern fails: no more data.  */
  109.       return 0;
  110.  
  111.     if (pattern_cursor == NULL)
  112.       /* The pattern fails: no star to adjust.  */
  113.       return 0;
  114.  
  115.     /* Restart pattern after star.  */
  116.  
  117.     pattern = pattern_cursor;
  118.     *result_cursor++ = *data_cursor;
  119.     *result_cursor = '\0';
  120.  
  121.     /* Rescan after copied char.  */
  122.  
  123.     data = ++data_cursor;
  124.       }
  125. }
  126.  
  127. /*------------------------------------------------------------------------.
  128. | Associated with a given file NAME, position FILE at the start of the      |
  129. | shell command portion of a shell archive file.  Scan file from position |
  130. | START.                                  |
  131. `------------------------------------------------------------------------*/
  132.  
  133. static int
  134. find_archive (const char *name, FILE *file, long start)
  135. {
  136.   char buffer[BUFSIZ];
  137.   long position;
  138.  
  139.   /* Results from star matcher.  */
  140.  
  141.   static char res1[BUFSIZ], res2[BUFSIZ], res3[BUFSIZ], res4[BUFSIZ];
  142.   static char *result[] = {res1, res2, res3, res4};
  143.  
  144.   fseek (file, start, 0);
  145.  
  146.   while (1)
  147.     {
  148.  
  149.       /* Record position of the start of this line.  */
  150.  
  151.       position = ftell (file);
  152.  
  153.       /* Read next line, fail if no more and no previous process.  */
  154.  
  155.       if (!fgets (buffer, BUFSIZ, file))
  156.     {
  157.       if (!start)
  158.         error (0, 0, "Found no shell commands in %s", name);
  159.       return 0;
  160.     }
  161.  
  162.       /* Bail out if we see C preprocessor commands or C comments.  */
  163.  
  164.       if (starting_with (buffer, "#include")
  165.       || starting_with (buffer, "# include")
  166.       || starting_with (buffer, "#define")
  167.       || starting_with (buffer, "# define")
  168.       || starting_with (buffer, "#ifdef")
  169.       || starting_with (buffer, "# ifdef")
  170.       || starting_with (buffer, "#ifndef")
  171.       || starting_with (buffer, "# ifndef")
  172.       || starting_with (buffer, "/*"))
  173.     {
  174.       error (0, 0, "%s looks like raw C code, not a shell archive", name);
  175.       return 0;
  176.     }
  177.  
  178.       /* Does this line start with a shell command or comment.  */
  179.  
  180.       if (starting_with (buffer, "#")
  181.       || starting_with (buffer, ":")
  182.       || starting_with (buffer, "echo ")
  183.       || starting_with (buffer, "sed ")
  184.       || starting_with (buffer, "cat ")
  185.       || starting_with (buffer, "if "))
  186.     {
  187.       fseek (file, position, 0);
  188.       return 1;
  189.     }
  190.  
  191.       /* Does this line say "Cut here".  */
  192.  
  193.       if (matched_by (buffer, "*CUT*HERE*", result) ||
  194.       matched_by (buffer, "*cut*here*", result) ||
  195.       matched_by (buffer, "*TEAR*HERE*", result) ||
  196.       matched_by (buffer, "*tear*here*", result) ||
  197.       matched_by (buffer, "*CUT*CUT*", result) ||
  198.       matched_by (buffer, "*cut*cut*", result))
  199.     {
  200.  
  201.       /* Read next line after "cut here", skipping blank lines.  */
  202.  
  203.       while (1)
  204.         {
  205.           position = ftell (file);
  206.  
  207.           if (!fgets (buffer, BUFSIZ, file))
  208.         {
  209.           error (0, 0, "Found no shell commands after 'cut' in %s",
  210.              name);
  211.           return 0;
  212.         }
  213.  
  214.           if (*buffer != '\n')
  215.         break;
  216.         }
  217.  
  218.       /* Win if line starts with a comment character of lower case
  219.          letter.  */
  220.  
  221.       if (*buffer == '#' || *buffer == ':'
  222.           || (('a' <= *buffer) && ('z' >= *buffer)))
  223.         {
  224.           fseek (file, position, 0);
  225.           return 1;
  226.         }
  227.  
  228.       /* Cut here message lied to us.  */
  229.  
  230.       error (0, 0, "%s is probably not a shell archive", name);
  231.       error (0, 0, "the 'cut' line was followed by: %s", buffer);
  232.       return 0;
  233.     }
  234.     }
  235. }
  236.  
  237. /*-----------------------------------------------------------------.
  238. | Unarchive a shar file provided on file NAME.  The file itself is |
  239. | provided on the already opened FILE.                   |
  240. `-----------------------------------------------------------------*/
  241.  
  242. static void
  243. unarchive_shar_file (const char *name, FILE *file)
  244. {
  245.   char buffer[SHELL_BUFFER_SIZE];
  246.   int character;
  247.   FILE *shell_process;
  248.   long current_position = 0;
  249.   char *more_to_read;
  250.  
  251.   while (find_archive (name, file, current_position))
  252.     {
  253.       printf ("%s:\n", name);
  254.       shell_process = popen (pass_c_flag ? "sh -s - -c" : "sh", "w");
  255.       if (!shell_process)
  256.     error (EXIT_FAILURE, errno, "Starting `sh' process");
  257.  
  258.       if (!continue_reading)
  259.     {
  260.       while (character = fgetc (file), character != EOF)
  261.         fputc (character, shell_process);
  262.       pclose (shell_process);
  263.       break;
  264.     }
  265.       else
  266.     {
  267.       while (more_to_read = fgets (buffer, SHELL_BUFFER_SIZE, file),
  268.          more_to_read != 0)
  269.         {
  270.           fputs (buffer, shell_process);
  271.           if (!strncmp (exit_string, buffer, exit_string_length))
  272.         break;
  273.         }
  274.       pclose (shell_process);
  275.  
  276.       if (more_to_read)
  277.         current_position = ftell (file);
  278.       else
  279.         break;
  280.     }
  281.     }
  282. }
  283.  
  284. /*-----------------------------.
  285. | Explain how to use program.  |
  286. `-----------------------------*/
  287.  
  288. static void
  289. usage (int status)
  290. {
  291.   if (status != EXIT_SUCCESS)
  292.     fprintf (stderr, "Try `%s --help' for more information.\n", program_name);
  293.   else
  294.     {
  295.       printf ("Usage: %s [OPTION]... [FILE]...\n", program_name);
  296.       fputs ("\
  297. Mandatory arguments to long options are mandatory for short options too.\n\
  298. \n\
  299.   -d, --directory=DIRECTORY   change to DIRECTORY before unpacking\n\
  300.   -c, --overwrite             pass -c flag to shar for overwriting files\n\
  301.   -e, --exit-0                same as `--split-at=\"exit 0\"'\n\
  302.   -E, --split-at=STRING       split concatenated shars after STRING\n\
  303.       --help                  display this help and exit\n\
  304.       --version               output version information and exit\n\
  305. \n\
  306. If no FILE, standard input is read.\n",
  307.          stdout);
  308.     }
  309.   exit (status);
  310. }
  311.  
  312. /*--------------------------------------.
  313. | Decode options and launch execution.  |
  314. `--------------------------------------*/
  315.  
  316. static const struct option long_options[] =
  317. {
  318.   {"directory", required_argument, NULL, 'd'},
  319.   {"exit-0", no_argument, NULL, 'e'}, 
  320.   {"overwrite", no_argument, NULL, 'c'}, 
  321.   {"split-at", required_argument, NULL, 'E'},
  322.  
  323.   {"help", no_argument, &show_help, 1},
  324.   {"version", no_argument, &show_version, 1},
  325.  
  326.   { 0, 0, 0, 0 },
  327. };
  328.  
  329. int
  330. main (int argc, char *const *argv)
  331. {
  332.   size_t size_read;
  333.   FILE *file;
  334.   char name_buffer[NAME_BUFFER_SIZE];
  335.   char copy_buffer[NAME_BUFFER_SIZE];
  336.   int optchar;
  337.  
  338.   program_name = argv[0];
  339.  
  340.   setbuf (stdout, NULL);
  341.   setbuf (stderr, NULL);
  342.  
  343.   if (current_directory = xgetcwd (), !current_directory)
  344.     error (EXIT_FAILURE, errno, "Cannot get current directory name");
  345.  
  346.   /* Process options.  */
  347.  
  348.   while (optchar = getopt_long (argc, argv, "E:cd:e", long_options, NULL),
  349.      optchar != EOF)
  350.     switch (optchar)
  351.       {
  352.       case '\0':
  353.     break;
  354.  
  355.       case 'c':
  356.     pass_c_flag = 1;
  357.     break;
  358.  
  359.       case 'd':
  360.     if (chdir (optarg) == -1)
  361.       error (2, 0, "Cannot chdir to `%s'", optarg);
  362.     break;
  363.  
  364.       case 'E':
  365.     exit_string = optarg;
  366.     /* Fall through.  */
  367.  
  368.       case 'e':
  369.     continue_reading = 1;
  370.     exit_string_length = strlen (exit_string);
  371.     break;
  372.  
  373.       default:
  374.     usage (EXIT_FAILURE);
  375.       }
  376.  
  377.   if (show_version)
  378.     {
  379.       printf ("GNU %s %s\n", PRODUCT, VERSION);
  380.       exit (EXIT_SUCCESS);
  381.     }
  382.  
  383.   if (show_help)
  384.     usage (EXIT_SUCCESS);
  385.  
  386.   if (optind < argc)
  387.     for (; optind < argc; optind++)
  388.       {
  389.     if (argv[optind][0] == '/')
  390.       strcpy (name_buffer, argv[optind]);
  391.     else
  392.       {
  393.         strcpy (name_buffer, current_directory);
  394.         strcat (name_buffer, "/");
  395.         strcat (name_buffer, argv[optind]);
  396.       }
  397.     if (file = fopen (name_buffer, "r"), !file)
  398.       error (EXIT_FAILURE, errno, name_buffer);
  399.     unarchive_shar_file (name_buffer, file);
  400.     fclose (file);
  401.       }
  402.   else
  403.     {
  404.       sprintf (name_buffer, "/tmp/unsh.%05d", (int) getpid ());
  405.       unlink (name_buffer);
  406.  
  407.       if (file = fopen (name_buffer, "w+"), !file)
  408.     error (EXIT_FAILURE, errno, name_buffer);
  409. #ifndef __MSDOS__
  410.       unlink (name_buffer);    /* will be deleted on fclose */
  411. #endif
  412.  
  413.       while (size_read = fread (copy_buffer, 1, sizeof (copy_buffer), stdin),
  414.          size_read != 0)
  415.     fwrite (copy_buffer, size_read, 1, file);
  416.       rewind (file);
  417.  
  418.       unarchive_shar_file ("standard input", file);
  419.  
  420.       fclose (file);
  421. #ifdef __MSDOS__
  422.       unlink (name_buffer);
  423. #endif
  424.     }
  425.  
  426.   exit (EXIT_SUCCESS);
  427. }
  428.