home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 8 / FreshFishVol8-CD2.bin / bbs / gnu / sharutils-4.1-src.lha / sharutils-4.1 / unshar.c < prev    next >
C/C++ Source or Header  |  1994-10-30  |  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             pas