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 >
Wrap
C/C++ Source or Header
|
1994-10-30
|
11KB
|
428 lines
/* Handle so called `shell archives'.
Copyright (C) 1994 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* Unpackage one or more shell archive files. The `unshar' program is a
filter which removes the front part of a file and passes the rest to
the `sh' command. It understands phrases like "cut here", and also
knows about shell comment characters and the Unix commands `echo',
`cat', and `sed'. */
#include "system.h"
#include "getopt.h"
/* Buffer size for holding a file name. */
#define NAME_BUFFER_SIZE 1024
/* Buffer size for shell process input. */
#define SHELL_BUFFER_SIZE 8196
#define EOL '\n'
/* The name this program was run with. */
const char *program_name;
/* If non-zero, display usage information and exit. */
static int show_help = 0;
/* If non-zero, print the version on standard output and exit. */
static int show_version = 0;
static int pass_c_flag = 0;
static int continue_reading = 0;
static const char *exit_string = "exit 0";
static size_t exit_string_length;
static char *current_directory;
/*-------------------------------------------------------------------------.
| Match the leftmost part of a string. Returns 1 if initial characters of |
| DATA match PATTERN exactly; else 0. |
`-------------------------------------------------------------------------*/
static int
starting_with (const char *data, const char *pattern)
{
const char *pattern_cursor, *data_cursor;
pattern_cursor = pattern;
data_cursor = data;
do
{
if (*pattern_cursor == '\0')
return 1;
}
while (*pattern_cursor++ == *data_cursor++);
return 0;
}
/*-------------------------------------------------------------------------.
| For a DATA string and a PATTERN containing one or more embedded |
| asterisks (matching any number of characters), return non-zero if the |
| match succeeds, and set RESULT_ARRAY[I] to the characters matched by the |
| I'th *. |
`-------------------------------------------------------------------------*/
static int
matched_by (const char *data, const char *pattern, char **result_array)
{
const char *pattern_cursor = NULL;
const char *data_cursor = NULL;
char *result_cursor = NULL;
int number_of_results = 0;
while (1)
if (*pattern == '*')
{
pattern_cursor = ++pattern;
data_cursor = data;
result_cursor = result_array[number_of_results++];
*result_cursor = '\0';
}
else if (*data == *pattern)
{
if (*pattern == '\0')
/* The pattern matches. */
return 1;
pattern++;
data++;
}
else
{
if (*data == '\0')
/* The pattern fails: no more data. */
return 0;
if (pattern_cursor == NULL)
/* The pattern fails: no star to adjust. */
return 0;
/* Restart pattern after star. */
pattern = pattern_cursor;
*result_cursor++ = *data_cursor;
*result_cursor = '\0';
/* Rescan after copied char. */
data = ++data_cursor;
}
}
/*------------------------------------------------------------------------.
| Associated with a given file NAME, position FILE at the start of the |
| shell command portion of a shell archive file. Scan file from position |
| START. |
`------------------------------------------------------------------------*/
static int
find_archive (const char *name, FILE *file, long start)
{
char buffer[BUFSIZ];
long position;
/* Results from star matcher. */
static char res1[BUFSIZ], res2[BUFSIZ], res3[BUFSIZ], res4[BUFSIZ];
static char *result[] = {res1, res2, res3, res4};
fseek (file, start, 0);
while (1)
{
/* Record position of the start of this line. */
position = ftell (file);
/* Read next line, fail if no more and no previous process. */
if (!fgets (buffer, BUFSIZ, file))
{
if (!start)
error (0, 0, "Found no shell commands in %s", name);
return 0;
}
/* Bail out if we see C preprocessor commands or C comments. */
if (starting_with (buffer, "#include")
|| starting_with (buffer, "# include")
|| starting_with (buffer, "#define")
|| starting_with (buffer, "# define")
|| starting_with (buffer, "#ifdef")
|| starting_with (buffer, "# ifdef")
|| starting_with (buffer, "#ifndef")
|| starting_with (buffer, "# ifndef")
|| starting_with (buffer, "/*"))
{
error (0, 0, "%s looks like raw C code, not a shell archive", name);
return 0;
}
/* Does this line start with a shell command or comment. */
if (starting_with (buffer, "#")
|| starting_with (buffer, ":")
|| starting_with (buffer, "echo ")
|| starting_with (buffer, "sed ")
|| starting_with (buffer, "cat ")
|| starting_with (buffer, "if "))
{
fseek (file, position, 0);
return 1;
}
/* Does this line say "Cut here". */
if (matched_by (buffer, "*CUT*HERE*", result) ||
matched_by (buffer, "*cut*here*", result) ||
matched_by (buffer, "*TEAR*HERE*", result) ||
matched_by (buffer, "*tear*here*", result) ||
matched_by (buffer, "*CUT*CUT*", result) ||
matched_by (buffer, "*cut*cut*", result))
{
/* Read next line after "cut here", skipping blank lines. */
while (1)
{
position = ftell (file);
if (!fgets (buffer, BUFSIZ, file))
{
error (0, 0, "Found no shell commands after 'cut' in %s",
name);
return 0;
}
if (*buffer != '\n')
break;
}
/* Win if line starts with a comment character of lower case
letter. */
if (*buffer == '#' || *buffer == ':'
|| (('a' <= *buffer) && ('z' >= *buffer)))
{
fseek (file, position, 0);
return 1;
}
/* Cut here message lied to us. */
error (0, 0, "%s is probably not a shell archive", name);
error (0, 0, "the 'cut' line was followed by: %s", buffer);
return 0;
}
}
}
/*-----------------------------------------------------------------.
| Unarchive a shar file provided on file NAME. The file itself is |
| provided on the already opened FILE. |
`-----------------------------------------------------------------*/
static void
unarchive_shar_file (const char *name, FILE *file)
{
char buffer[SHELL_BUFFER_SIZE];
int character;
FILE *shell_process;
long current_position = 0;
char *more_to_read;
while (find_archive (name, file, current_position))
{
printf ("%s:\n", name);
shell_process = popen (pass_c_flag ? "sh -s - -c" : "sh", "w");
if (!shell_process)
error (EXIT_FAILURE, errno, "Starting `sh' process");
if (!continue_reading)
{
while (character = fgetc (file), character != EOF)
fputc (character, shell_process);
pclose (shell_process);
break;
}
else
{
while (more_to_read = fgets (buffer, SHELL_BUFFER_SIZE, file),
more_to_read != 0)
{
fputs (buffer, shell_process);
if (!strncmp (exit_string, buffer, exit_string_length))
break;
}
pclose (shell_process);
if (more_to_read)
current_position = ftell (file);
else
break;
}
}
}
/*-----------------------------.
| Explain how to use program. |
`-----------------------------*/
static void
usage (int status)
{
if (status != EXIT_SUCCESS)
fprintf (stderr, "Try `%s --help' for more information.\n", program_name);
else
{
printf ("Usage: %s [OPTION]... [FILE]...\n", program_name);
fputs ("\
Mandatory arguments to long options are mandatory for short options too.\n\
\n\
-d, --directory=DIRECTORY change to DIRECTORY before unpacking\n\
-c, --overwrite pas