1219019Sgabor/* Handle so called `shell archives'.
2219019Sgabor   Copyright (C) 1994, 1995 Free Software Foundation, Inc.
3219019Sgabor
4219019Sgabor   This program is free software; you can redistribute it and/or modify
5219019Sgabor   it under the terms of the GNU General Public License as published by
6219019Sgabor   the Free Software Foundation; either version 2, or (at your option)
7219019Sgabor   any later version.
8219019Sgabor
9219019Sgabor   This program is distributed in the hope that it will be useful,
10219019Sgabor   but WITHOUT ANY WARRANTY; without even the implied warranty of
11219019Sgabor   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12219019Sgabor   GNU General Public License for more details.
13219019Sgabor
14219019Sgabor   You should have received a copy of the GNU General Public License
15219019Sgabor   along with this program; if not, write to the Free Software Foundation,
16219019Sgabor   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17219019Sgabor*/
18219019Sgabor
19219019Sgabor#include "system.h"
20219019Sgabor
21219019Sgaborstatic const char *cut_mark_line
22219019Sgabor  = "---- Cut Here and feed the following to sh ----\n";
23219019Sgabor
24219019Sgabor/* Delimiter to put after each file.  */
25219019Sgabor#define	DEFAULT_HERE_DELIMITER "SHAR_EOF"
26219019Sgabor
27219019Sgabor/* Character which goes in front of each line.  */
28219019Sgabor#define DEFAULT_LINE_PREFIX_1 'X'
29219019Sgabor
30219019Sgabor/* Character which goes in front of each line if here_delimiter[0] ==
31219019Sgabor   DEFAULT_LINE_PREFIX_1.  */
32219019Sgabor#define DEFAULT_LINE_PREFIX_2 'Y'
33219019Sgabor
34219019Sgabor/* Shell command able to count characters from its standard input.  We
35219019Sgabor   have to take care for the locale setting because wc in multi-byte
36219019Sgabor   character environments get different results.  */
37219019Sgabor#define CHARACTER_COUNT_COMMAND "LC_ALL= LC_CTYPE= LANG= wc -c <"
38219019Sgabor
39219019Sgabor/* Command computing MD5 sumof specified file.  Because there is not
40219019Sgabor   official standard for this we have to restrict ourself to a few
41219019Sgabor   versions: GNU md5sum from GNU fileutils and the version by
42219019Sgabor   Plumb/Lankester.  */
43219019Sgabor#define MD5SUM_COMMAND "md5sum"
44219019Sgabor
45219019Sgabor/* Maximum length for a text line before it is considered binary.  */
46219019Sgabor#define MAXIMUM_NON_BINARY_LINE 200
47219019Sgabor
48219019Sgabor/* System related declarations.  */
49219019Sgabor
50219019Sgabor#include <ctype.h>
51219019Sgabor
52219019Sgabor#if STDC_HEADERS
53219019Sgabor# define ISASCII(Char) 1
54219019Sgabor#else
55219019Sgabor# ifdef isascii
56219019Sgabor#  define ISASCII(Char) isascii (Char)
57219019Sgabor# else
58219019Sgabor#  if HAVE_ISASCII
59219019Sgabor#   define ISASCII(Char) isascii (Char)
60219019Sgabor#  else
61219019Sgabor#   define ISASCII(Char) ((Char) & 0x7f == (unsigned char) (Char))
62219019Sgabor#  endif
63219019Sgabor# endif
64219019Sgabor#endif
65219019Sgabor
66219019Sgabor#include <time.h>
67219019Sgabor
68219019Sgaborstruct tm *localtime ();
69219019Sgabor
70219019Sgabor#if !NO_WALKTREE
71219019Sgabor
72219019Sgabor/* Declare directory reading routines and structures.  */
73219019Sgabor
74219019Sgabor#ifdef __MSDOS__
75219019Sgabor# include "msd_dir.h"
76219019Sgabor# define NAMLEN(dirent) ((dirent)->d_namlen)
77219019Sgabor#else
78219019Sgabor# if HAVE_DIRENT_H
79219019Sgabor#  include <dirent.h>
80219019Sgabor#  define NAMLEN(dirent) (strlen((dirent)->d_name))
81219019Sgabor# else
82219019Sgabor#  define dirent direct
83219019Sgabor#  define NAMLEN(dirent) ((dirent)->d_namlen)
84219019Sgabor#  if HAVE_SYS_NDIR_H
85219019Sgabor#   include <sys/ndir.h>
86219019Sgabor#  endif
87219019Sgabor#  if HAVE_SYS_DIR_H
88219019Sgabor#   include <sys/dir.h>
89219019Sgabor#  endif
90219019Sgabor#  if HAVE_NDIR_H
91219019Sgabor#   include <ndir.h>
92219019Sgabor#  endif
93219019Sgabor# endif
94219019Sgabor#endif
95219019Sgabor
96219019SgaborDIR *opendir ();
97219019Sgaborstruct dirent *readdir ();
98219019Sgabor
99219019Sgabor#endif /* !NO_WALKTREE */
100219019Sgabor
101219019Sgabor/* Option variables.  */
102219019Sgabor
103219019Sgabor#include "getopt.h"
104219019Sgabor#include "md5.h"
105219019Sgabor
106219019Sgabor/* No Brown-Shirt mode.  */
107219019Sgaborstatic int vanilla_operation_mode = 0;
108219019Sgabor
109219019Sgabor/* Mixed text and binary files.  */
110219019Sgaborstatic int mixed_uuencoded_file_mode = -1;
111219019Sgabor
112219019Sgabor/* Flag for binary files.  */
113219019Sgaborstatic int uuencoded_file_mode = -1;
114219019Sgabor
115219019Sgabor/* Run input files through gzip (requires uuencoded_file_mode).  */
116219019Sgaborstatic int gzipped_file_mode = -1;
117219019Sgabor
118219019Sgabor/* -N option to gzip.  */
119219019Sgaborstatic int gzip_compression_level = 9;
120219019Sgabor
121219019Sgabor/* Run input files through compress (requires uuencoded_file_mode).  */
122219019Sgaborstatic int compressed_file_mode = -1;
123219019Sgabor
124219019Sgabor/* -bN option to compress */
125219019Sgaborstatic int bits_per_compressed_byte = 12;
126219019Sgabor
127219019Sgabor/* Generate $shar_touch commands.  */
128219019Sgaborstatic int timestamp_mode = 1;
129219019Sgabor
130219019Sgabor/* Option to provide wc checking.  */
131219019Sgaborstatic int character_count_mode = 1;
132219019Sgabor
133219019Sgabor/* Option to provide MD5 sum checking.  */
134219019Sgaborstatic int md5_count_mode = 1;
135219019Sgabor
136219019Sgabor/* Use temp file instead of pipe to feed uudecode.  This gives better
137219019Sgabor   error detection, at expense of disk space.  This is also necessary for
138219019Sgabor   those versions of uudecode unwilling to read their standard input.  */
139219019Sgaborstatic int inhibit_piping_mode = 0;
140219019Sgabor
141219019Sgabor/* --no-i18n option to prevent internationalized shell archives.  */
142219019Sgaborstatic int no_i18n;
143219019Sgabor
144219019Sgabor/* Character to get at the beginning of each line.  */
145219019Sgaborstatic int line_prefix;
146219019Sgabor
147219019Sgabor/* Option to generate "Archive-name:" headers.  */
148219019Sgaborstatic int net_headers_mode = 0;
149219019Sgabor
150219019Sgabor/* Documentation name for archive.  */
151219019Sgaborstatic char *archive_name = NULL;
152219019Sgabor
153219019Sgabor/* Option to provide append feedback at shar time.  */
154219019Sgaborstatic int quiet_mode = 0;
155219019Sgabor
156219019Sgabor/* Option to provide extract feedback at unshar time.  */
157219019Sgaborstatic int quiet_unshar_mode = 0;
158219019Sgabor
159219019Sgabor/* Pointer to delimiter string.  */
160219019Sgaborstatic const char *here_delimiter = DEFAULT_HERE_DELIMITER;
161219019Sgabor
162219019Sgabor/* Value of strlen (here_delimiter).  */
163219019Sgaborstatic size_t here_delimiter_length;
164219019Sgabor
165219019Sgabor/* Use line_prefix even when first char does not force it.  */
166219019Sgaborstatic int mandatory_prefix_mode = 0;
167219019Sgabor
168219019Sgabor/* Option to provide cut mark.  */
169219019Sgaborstatic int cut_mark_mode = 0;
170219019Sgabor
171219019Sgabor/* Check if file exists.  */
172219019Sgaborstatic int check_existing_mode = 1;
173219019Sgabor
174219019Sgabor/* Interactive overwrite.  */
175219019Sgaborstatic int query_user_mode = 0;
176219019Sgabor
177219019Sgabor/* Allow positional parameters.  */
178219019Sgaborstatic int intermixed_parameter_mode = 0;
179219019Sgabor
180219019Sgabor/* Strip directories from filenames.  */
181219019Sgaborstatic int basename_mode;
182219019Sgabor
183219019Sgabor/* Switch for debugging on.  */
184219019Sgabor#if DEBUG
185219019Sgaborstatic int debugging_mode = 0;
186219019Sgabor#endif
187219019Sgabor
188219019Sgabor/* Split files in the middle.  */
189219019Sgaborstatic int split_file_mode = 0;
190219019Sgabor
191219019Sgabor/* File size limit in kilobytes.  */
192219019Sgaborstatic unsigned file_size_limit = 0;
193219019Sgabor
194219019Sgabor/* Other global variables.  */
195219019Sgabor
196219019Sgabor/* The name this program was run with. */
197219019Sgaborconst char *program_name;
198219019Sgabor
199219019Sgabor/* If non-zero, display usage information and exit.  */
200219019Sgaborstatic int show_help = 0;
201219019Sgabor
202219019Sgabor/* If non-zero, print the version on standard output and exit.  */
203219019Sgaborstatic int show_version = 0;
204219019Sgabor
205219019Sgabor/* File onto which the shar script is being written.  */
206219019Sgaborstatic FILE *output = NULL;
207219019Sgabor
208219019Sgabor/* Position for archive type message.  */
209219019Sgaborstatic off_t archive_type_position;
210219019Sgabor
211219019Sgabor/* Position for first file in the shar file.  */
212219019Sgaborstatic long first_file_position;
213219019Sgabor
214219019Sgabor/* Base for output filename.  FIXME: No fix limit in GNU... */
215219019Sgaborstatic char output_base_name[50];
216219019Sgabor
217219019Sgabor/* Actual output filename.  FIXME: No fix limit in GNU... */
218219019Sgaborstatic char output_filename[50];
219219019Sgabor
220219019Sgaborstatic char *submitter_address = NULL;
221219019Sgabor
222219019Sgabor/* Output file ordinal.  FIXME: also flag for -o.  */
223219019Sgaborstatic int part_number = 0;
224219019Sgabor
225219019Sgabor/* Table saying whether each character is binary or not.  */
226219019Sgaborstatic unsigned char byte_is_binary[256];
227219019Sgabor
228219019Sgabor/* For checking file type and access modes.  */
229219019Sgaborstatic struct stat struct_stat;
230219019Sgabor
231219019Sgabor/* Nonzero if special NLS option (--print-text-domain-dir) is selected.  */
232219019Sgaborstatic int print_text_dom_dir;
233219019Sgabor
234219019Sgabor/* The number used to make the intermediate files unique.  */
235219019Sgaborstatic int sharpid;
236219019Sgabor
237219019Sgabor#if DEBUG
238219019Sgabor# define DEBUG_PRINT(Format, Value) \
239219019Sgabor    if (debugging_mode) printf(Format, Value)
240219019Sgabor#else
241219019Sgabor# define DEBUG_PRINT(Format, Value)
242219019Sgabor#endif
243219019Sgabor
244219019Sgaborstatic void open_output __P ((void));
245219019Sgaborstatic void close_output __P ((void));
246219019Sgaborstatic void usage __P ((int));
247219019Sgabor
248219019Sgabor/* Walking tree routines.  */
249219019Sgabor
250219019Sgabor/* Define a type just for easing ansi2knr's life.  */
251219019Sgabortypedef int (*walker_t) __P ((const char *, const char *));
252219019Sgabor
253219019Sgabor#if !NO_WALKTREE
254219019Sgabor
255219019Sgabor/*--------------------------------------------------------------------------.
256219019Sgabor| Recursively call ROUTINE on each entry, down the directory tree.  NAME    |
257219019Sgabor| is the path to explore.  RESTORE_NAME is the name that will be later	    |
258219019Sgabor| relative to the unsharing directory.  ROUTINE may also assume		    |
259219019Sgabor| struct_stat is set, it accepts updated values for NAME and RESTORE_NAME.  |
260219019Sgabor`--------------------------------------------------------------------------*/
261219019Sgabor
262219019Sgaborstatic int
263219019Sgaborwalkdown (routine, local_name, restore_name)
264219019Sgabor     walker_t routine;
265219019Sgabor     const char *local_name;
266219019Sgabor     const char *restore_name;
267219019Sgabor{
268219019Sgabor  DIR *directory;		/* directory being scanned */
269219019Sgabor  struct dirent *entry;		/* current entry in directory */
270219019Sgabor  int status;			/* status to return */
271219019Sgabor
272219019Sgabor  char *local_name_copy;	/* writeable copy of local_name */
273219019Sgabor  size_t local_name_length;	/* number of characters in local_name_copy */
274219019Sgabor  size_t sizeof_local_name;	/* allocated size of local_name_copy */
275219019Sgabor
276219019Sgabor  char *restore_name_copy;	/* writeable copy of restore_name */
277219019Sgabor  size_t restore_name_length;	/* number of characters in restore_name_copy */
278219019Sgabor  size_t sizeof_restore_name;	/* allocated size of restore_name_copy */
279219019Sgabor
280219019Sgabor  if (stat (local_name, &struct_stat))
281219019Sgabor    {
282219019Sgabor      error (0, errno, local_name);
283219019Sgabor      return 1;
284219019Sgabor    }
285219019Sgabor
286219019Sgabor  if (!S_ISDIR (struct_stat.st_mode & S_IFMT))
287219019Sgabor    return (*routine) (local_name, restore_name);
288219019Sgabor
289219019Sgabor  if (directory = opendir (local_name), !directory)
290219019Sgabor    {
291219019Sgabor      error (0, errno, local_name);
292219019Sgabor      return 1;
293219019Sgabor    }
294219019Sgabor
295219019Sgabor  status = 0;
296219019Sgabor
297219019Sgabor  local_name_copy = xstrdup (local_name);
298219019Sgabor  local_name_length = strlen (local_name_copy);
299219019Sgabor  sizeof_local_name = local_name_length + 1;
300219019Sgabor
301219019Sgabor  restore_name_copy = xstrdup (restore_name);
302219019Sgabor  restore_name_length = strlen (restore_name_copy);
303219019Sgabor  sizeof_restore_name = restore_name_length + 1;
304219019Sgabor
305219019Sgabor  while (!status && (entry = readdir (directory), entry))
306219019Sgabor    if (strcmp (entry->d_name, ".") && strcmp (entry->d_name, ".."))
307219019Sgabor      {
308219019Sgabor	int added_size = 1 + NAMLEN (entry);
309219019Sgabor
310219019Sgabor	/* Update file names, reallocating them as required.  */
311219019Sgabor
312219019Sgabor	if (local_name_length + added_size + 1 > sizeof_local_name)
313219019Sgabor	  {
314219019Sgabor	    sizeof_local_name = local_name_length + added_size + 1;
315219019Sgabor	    local_name_copy = (char *)
316219019Sgabor	      xrealloc (local_name_copy, sizeof_local_name);
317219019Sgabor	  }
318219019Sgabor	sprintf (local_name_copy + local_name_length, "/%s", entry->d_name);
319219019Sgabor
320219019Sgabor	if (restore_name_length + added_size + 1 > sizeof_restore_name)
321219019Sgabor	  {
322219019Sgabor	    sizeof_restore_name = restore_name_length + added_size + 1;
323219019Sgabor	    restore_name_copy = (char *)
324219019Sgabor	      xrealloc (restore_name_copy, sizeof_restore_name);
325219019Sgabor	  }
326219019Sgabor	sprintf (restore_name_copy + restore_name_length, "/%s",
327219019Sgabor		 entry->d_name);
328219019Sgabor
329219019Sgabor	/* Avoid restoring `./xxx' when shar'ing `.'.  */
330219019Sgabor
331219019Sgabor	if (!strncmp (restore_name, "./", 2))
332219019Sgabor	  {
333219019Sgabor	    strcpy (restore_name_copy, restore_name_copy + 2);
334219019Sgabor	    restore_name_length -= 2;
335219019Sgabor	  }
336219019Sgabor
337219019Sgabor	status = walkdown (routine, local_name_copy, restore_name_copy);
338219019Sgabor      }
339219019Sgabor
340219019Sgabor  /* Clean up.  */
341219019Sgabor
342219019Sgabor  if (sizeof_local_name > 0)
343219019Sgabor    free (local_name_copy);
344219019Sgabor  if (sizeof_restore_name > 0)
345219019Sgabor    free (restore_name_copy);
346219019Sgabor
347219019Sgabor#if CLOSEDIR_VOID
348219019Sgabor  closedir (directory);
349219019Sgabor#else
350219019Sgabor  if (closedir (directory))
351219019Sgabor    {
352219019Sgabor      error (0, errno, local_name);
353219019Sgabor      return 1;
354219019Sgabor    }
355219019Sgabor#endif
356219019Sgabor
357219019Sgabor  return status;
358219019Sgabor}
359219019Sgabor
360219019Sgabor#endif /* !NO_WALKTREE */
361219019Sgabor
362219019Sgabor/*------------------------------------------------------------------.
363219019Sgabor| Walk through the directory tree, calling ROUTINE for each entry.  |
364219019Sgabor| ROUTINE may also assume struct_stat is set.			    |
365219019Sgabor`------------------------------------------------------------------*/
366219019Sgabor
367219019Sgaborstatic int
368219019Sgaborwalktree (routine, local_name)
369219019Sgabor     walker_t routine;
370219019Sgabor     const char *local_name;
371219019Sgabor{
372219019Sgabor  const char *restore_name;
373219019Sgabor  char *local_name_copy;
374219019Sgabor  char *cursor;
375219019Sgabor  int status;
376219019Sgabor  int len;
377219019Sgabor
378219019Sgabor  /* Remove crumb at end.  */
379219019Sgabor
380219019Sgabor  len = strlen (local_name);
381219019Sgabor  local_name_copy = (char *) alloca (len + 1);
382219019Sgabor  memcpy (local_name_copy, local_name, len + 1);
383219019Sgabor  cursor = local_name_copy + len - 1;
384219019Sgabor  while (*cursor == '/' && cursor > local_name_copy)
385219019Sgabor    *cursor-- = '\0';
386219019Sgabor
387219019Sgabor  /* Remove crumb at beginning.  */
388219019Sgabor
389219019Sgabor  if (basename_mode)
390219019Sgabor    restore_name = basename (local_name_copy);
391219019Sgabor  else if (!strncmp (local_name_copy, "./", 2))
392219019Sgabor    restore_name = local_name_copy + 2;
393219019Sgabor  else
394219019Sgabor    restore_name = local_name_copy;
395219019Sgabor
396219019Sgabor#if NO_WALKTREE
397219019Sgabor
398219019Sgabor  /* Just act on current entry.  */
399219019Sgabor
400219019Sgabor  status = stat (local_name_copy, &struct_stat);
401219019Sgabor  if (status != 0)
402219019Sgabor    error (0, errno, local_name_copy);
403219019Sgabor  else
404219019Sgabor    status = (*routine) (local_name_copy, restore_name);
405219019Sgabor
406219019Sgabor#else
407219019Sgabor
408219019Sgabor  /* Walk recursively.  */
409219019Sgabor
410219019Sgabor  status = walkdown (routine, local_name_copy, restore_name);
411219019Sgabor
412219019Sgabor#endif
413219019Sgabor
414219019Sgabor  return status;
415219019Sgabor}
416219019Sgabor
417219019Sgabor/* Generating parts of shar file.  */
418219019Sgabor
419219019Sgabor/*---------------------------------------------------------------------.
420219019Sgabor| Build a `drwxrwxrwx' string corresponding to MODE into MODE_STRING.  |
421219019Sgabor`---------------------------------------------------------------------*/
422219019Sgabor
423219019Sgaborstatic char *
424219019Sgabormode_string (mode)
425219019Sgabor     unsigned mode;
426219019Sgabor{
427219019Sgabor  static char result[12];
428219019Sgabor
429219019Sgabor  strcpy (result, "----------");
430219019Sgabor
431219019Sgabor  if (mode & 00400)
432219019Sgabor    result[1] = 'r';
433219019Sgabor  if (mode & 00200)
434219019Sgabor    result[2] = 'w';
435219019Sgabor  if (mode & 00100)
436219019Sgabor    result[3] = 'x';
437219019Sgabor  if (mode & 04000)
438219019Sgabor    result[3] = 's';
439219019Sgabor  if (mode & 00040)
440219019Sgabor    result[4] = 'r';
441219019Sgabor  if (mode & 00020)
442219019Sgabor    result[5] = 'w';
443219019Sgabor  if (mode & 00010)
444219019Sgabor    result[6] = 'x';
445219019Sgabor  if (mode & 02000)
446219019Sgabor    result[6] = 's';
447219019Sgabor  if (mode & 00004)
448219019Sgabor    result[7] = 'r';
449219019Sgabor  if (mode & 00002)
450219019Sgabor    result[8] = 'w';
451219019Sgabor  if (mode & 00001)
452219019Sgabor    result[9] = 'x';
453219019Sgabor
454219019Sgabor  return result;
455219019Sgabor}
456219019Sgabor
457219019Sgabor/*-----------------------------------------------------------------------.
458219019Sgabor| Generate shell code which, at *unshar* time, will study the properties |
459219019Sgabor| of the unpacking system and set some variables accordingly.		 |
460219019Sgabor`-----------------------------------------------------------------------*/
461219019Sgabor
462219019Sgaborstatic void
463219019Sgaborgenerate_configure ()
464219019Sgabor{
465219019Sgabor  if (no_i18n)
466219019Sgabor    fputs ("echo=echo\n", output);
467219019Sgabor  else
468219019Sgabor    {
469219019Sgabor      fprintf (output, "\
470219019Sgaborsave_IFS=\"${IFS}\"\n\
471219019SgaborIFS=\"${IFS}:\"\n\
472219019Sgaborgettext_dir=FAILED\n\
473219019Sgaborlocale_dir=FAILED\n\
474219019Sgaborfirst_param=\"$1\"\n\
475219019Sgaborfor dir in $PATH\n\
476219019Sgabordo\n\
477219019Sgabor  if test \"$gettext_dir\" = FAILED && test -f $dir/gettext \\\n\
478219019Sgabor     && ($dir/gettext --version >/dev/null 2>&1)\n\
479219019Sgabor  then\n\
480219019Sgabor    set `$dir/gettext --version 2>&1`\n\
481219019Sgabor    if test \"$3\" = GNU\n\
482219019Sgabor    then\n\
483219019Sgabor      gettext_dir=$dir\n\
484219019Sgabor    fi\n\
485219019Sgabor  fi\n\
486219019Sgabor  if test \"$locale_dir\" = FAILED && test -f $dir/%s \\\n\
487219019Sgabor     && ($dir/%s --print-text-domain-dir >/dev/null 2>&1)\n\
488219019Sgabor  then\n\
489219019Sgabor    locale_dir=`$dir/%s --print-text-domain-dir`\n\
490219019Sgabor  fi\n\
491219019Sgabordone\n\
492219019SgaborIFS=\"$save_IFS\"\n\
493219019Sgaborif test \"$locale_dir\" = FAILED || test \"$gettext_dir\" = FAILED\n\
494219019Sgaborthen\n\
495219019Sgabor  echo=echo\n\
496219019Sgaborelse\n\
497219019Sgabor  TEXTDOMAINDIR=$locale_dir\n\
498219019Sgabor  export TEXTDOMAINDIR\n\
499219019Sgabor  TEXTDOMAIN=sharutils\n\
500  export TEXTDOMAIN\n\
501  echo=\"$gettext_dir/gettext -s\"\n\
502fi\n",
503	       "shar", "shar", "shar");
504      /* Above the name of the program of the package which supports the
505	 --print-text-domain-dir option has to be given.  */
506    }
507
508  if (query_user_mode)
509    if (vanilla_operation_mode)
510      fputs ("\
511shar_tty= shar_n= shar_c='\n\
512'\n",
513	     output);
514    else
515      {
516
517	/* Check if /dev/tty exists.  If yes, define shar_tty to
518	   `/dev/tty', else, leave it empty.  */
519
520	fputs ("\
521if test -n \"`ls /dev/tty 2>/dev/null`\"; then\n\
522  shar_tty=/dev/tty\n\
523else\n\
524  shar_tty=\n\
525fi\n",
526	       output);
527
528	/* Try to find a way to echo a message without newline.  Set
529	   shar_n to `-n' or nothing for an echo option, and shar_c to
530	   `\c' or nothing for a string terminator.  */
531
532	fputs ("\
533if (echo \"testing\\c\"; echo 1,2,3) | grep c >/dev/null; then\n\
534  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then\n\
535    shar_n= shar_c='\n\
536'\n\
537  else\n\
538    shar_n=-n shar_c=\n\
539  fi\n\
540else\n\
541  shar_n= shar_c='\\c'\n\
542fi\n",
543	       output);
544      }
545
546  if (timestamp_mode)
547    {
548      const char *file = "$$.touch";
549      const char *stamp1 = "200112312359.59";
550      const char *stamp2 = "123123592001.59";
551      const char *stamp2tr = "123123592001.5"; /* old SysV 14-char limit */
552      const char *stamp3 = "1231235901";
553
554      /* Set the shell variable shar_touch to an appropriate invocation
555	 of `touch' if the touch program is proven able to restore dates.
556	 Otherwise, set shar_touch to `:' and issue a warning.  */
557
558      fprintf (output, "\
559if touch -am -t %s %s >/dev/null 2>&1 && test ! -f %s -a -f %s; then\n\
560  shar_touch='touch -am -t $1$2$3$4$5$6.$7 \"$8\"'\n\
561elif touch -am %s %s >/dev/null 2>&1 && test ! -f %s -a ! -f %s -a -f %s; then\n\
562  shar_touch='touch -am $3$4$5$6$1$2.$7 \"$8\"'\n\
563elif touch -am %s %s >/dev/null 2>&1 && test ! -f %s -a -f %s; then\n\
564  shar_touch='touch -am $3$4$5$6$2 \"$8\"'\n\
565else\n\
566  shar_touch=:\n\
567  echo\n\
568  $echo '%s'\n\
569  $echo \"%s\"\n\
570  echo\n\
571fi\n\
572rm -f %s %s %s %s %s\n\
573#\n",
574	       stamp1, file, stamp1, file,
575	       stamp2, file, stamp2, stamp2tr, file,
576	       stamp3, file, stamp3, file,
577	       N_("\
578WARNING: not restoring timestamps.  Consider getting and"),
579	       N_("\
580installing GNU \\`touch', distributed in GNU File Utilities..."),
581	       stamp1, stamp2, stamp2tr, stamp3, file);
582    }
583
584  if (!split_file_mode || part_number == 1)
585    {
586      /* Create locking directory.  */
587      fprintf (output, "\
588if mkdir _sh%05d; then\n\
589  $echo 'x -' '%s'\n\
590else\n\
591  $echo '%s'\n\
592  exit 1\n\
593fi\n",
594	       sharpid, N_("creating lock directory"),
595	       N_("failed to create lock directory"));
596    }
597}
598
599/*---.
600| ?  |
601`---*/
602
603/* Ridiculously enough.  FIXME: No fix limit in GNU...  */
604#define MAX_MKDIR_ALREADY	128
605
606char *mkdir_already[MAX_MKDIR_ALREADY];
607int mkdir_already_count = 0;
608
609static void
610generate_mkdir (path)
611     const char *path;
612{
613  int counter;
614  char *cursor;
615
616  /* If already generated code for this dir creation, don't do again.  */
617
618  for (counter = 0; counter < mkdir_already_count; counter++)
619    if (!strcmp (path, mkdir_already[counter]))
620      return;
621
622  /* Haven't done this one.  */
623
624  if (mkdir_already_count == MAX_MKDIR_ALREADY)
625    error (EXIT_FAILURE, 0, _("Too many directories for mkdir generation"));
626  cursor = mkdir_already[mkdir_already_count++] = xmalloc (strlen (path) + 1);
627  strcpy (cursor, path);
628
629  /* Generate the text.  */
630
631  fprintf (output, "if test ! -d '%s'; then\n", path);
632  if (!quiet_unshar_mode)
633    fprintf (output, "  $echo 'x -' '%s' '%s'\n",
634	     N_("creating directory"), path);
635  fprintf (output, "  mkdir '%s'\n", path);
636  fputs ("fi\n", output);
637}
638
639/*---.
640| ?  |
641`---*/
642
643static void
644generate_mkdir_script (path)
645     const char *path;
646{
647  char *cursor;
648
649  for (cursor = strchr (path, '/'); cursor; cursor = strchr (cursor + 1, '/'))
650    {
651
652      /* Avoid empty string if leading or double '/'.  */
653
654      if (cursor == path || *(cursor - 1) == '/')
655	continue;
656
657      /* Omit '.'.  */
658
659      if (cursor[-1] == '.' && (cursor == path + 1 || cursor[-2] == '/'))
660	continue;
661
662      /* Temporarily terminate string.  FIXME!  */
663
664      *cursor = 0;
665      generate_mkdir (path);
666      *cursor = '/';
667    }
668}
669
670/* Walking routines.  */
671
672/*---.
673| ?  |
674`---*/
675
676static int
677check_accessibility (local_name, restore_name)
678     const char *local_name;
679     const char *restore_name;
680{
681  if (access (local_name, 4))
682    {
683      error (0, 0, _("Cannot access %s"), local_name);
684      return 1;
685    }
686
687  return 0;
688}
689
690/*---.
691| ?  |
692`---*/
693
694static int
695generate_one_header_line (local_name, restore_name)
696     const char *local_name;
697     const char *restore_name;
698{
699  fprintf (output, "# %6ld %s %s\n", struct_stat.st_size,
700	   mode_string (struct_stat.st_mode), restore_name);
701  return 0;
702}
703
704/*---.
705| ?  |
706`---*/
707
708static void
709generate_full_header (argc, argv)
710     int argc;
711     char *const *argv;
712{
713  char *current_directory;
714  time_t now;
715  struct tm *local_time;
716  char buffer[80];		/* FIXME: No fix limit in GNU... */
717  int warned_once;
718  int counter;
719
720  warned_once = 0;
721  for (counter = 0; counter < argc; counter++)
722    {
723
724      /* Skip positional parameters.  */
725
726      if (intermixed_parameter_mode &&
727	  (strcmp (argv[counter], "-B") == 0 ||
728	   strcmp (argv[counter], "-T") == 0 ||
729	   strcmp (argv[counter], "-M") == 0 ||
730	   strcmp (argv[counter], "-z") == 0 ||
731	   strcmp (argv[counter], "-Z") == 0 ||
732	   strcmp (argv[counter], "-C") == 0))
733	{
734	  if (!warned_once && strcmp (argv[counter], "-C") == 0)
735	    {
736	      error (0, 0, _("-C is being deprecated, use -Z instead"));
737	      warned_once = 1;
738	    }
739	  continue;
740	}
741
742      if (walktree (check_accessibility, argv[counter]))
743	exit (EXIT_FAILURE);
744    }
745
746  if (net_headers_mode)
747    {
748      fprintf (output, "Submitted-by: %s\n", submitter_address);
749      fprintf (output, "Archive-name: %s%s%02d\n\n",
750	       archive_name, (strchr (archive_name, '/')) ? "" : "/part",
751	       part_number ? part_number : 1);
752    }
753
754  if (cut_mark_mode)
755    fputs (cut_mark_line, output);
756  fputs ("\
757#!/bin/sh\n",
758	 output);
759  if (archive_name)
760    fprintf (output, "\
761# This is %s, a shell archive (produced by GNU %s %s)\n",
762	     archive_name, PACKAGE, VERSION);
763  else
764    fprintf (output, "\
765# This is a shell archive (produced by GNU %s %s).\n",
766	     PACKAGE, VERSION);
767  fputs ("\
768# To extract the files from this archive, save it to some FILE, remove\n\
769# everything before the `!/bin/sh' line above, then type `sh FILE'.\n\
770#\n",
771	 output);
772
773  time (&now);
774  local_time = localtime (&now);
775  strftime (buffer, 79, "%Y-%m-%d %H:%M %Z", local_time);
776  fprintf (output, "\
777# Made on %s by <%s>.\n",
778	   buffer, submitter_address);
779
780  current_directory = xgetcwd ();
781  if (current_directory)
782    {
783      fprintf (output, "\
784# Source directory was `%s'.\n",
785	       current_directory);
786      free (current_directory);
787    }
788  else
789    error (0, errno, _("Cannot get current directory name"));
790
791  if (check_existing_mode)
792    fputs ("\
793#\n\
794# Existing files will *not* be overwritten unless `-c' is specified.\n",
795	   output);
796  else if (query_user_mode)
797    fputs ("\
798#\n\
799# existing files MAY be overwritten\n",
800	   output);
801  else
802    fputs ("\
803#\n\
804# existing files WILL be overwritten\n",
805	   output);
806
807  if (query_user_mode)
808    fputs ("\
809# The unsharer will be INTERACTIVELY queried.\n",
810	   output);
811
812  if (vanilla_operation_mode)
813    {
814      fputs ("\
815# This format requires very little intelligence at unshar time.\n\
816# ",
817	     output);
818      if (check_existing_mode || split_file_mode)
819	fputs ("\"if test\", ", output);
820      if (split_file_mode)
821	fputs ("\"cat\", \"rm\", ", output);
822      fputs ("\"echo\", \"mkdir\", and \"sed\" may be needed.\n", output);
823    }
824
825  if (split_file_mode)
826    {
827
828      /* May be split, explain.  */
829
830      fputs ("#\n", output);
831      archive_type_position = ftell (output);
832      fprintf (output, "%-75s\n%-75s\n", "#", "#");
833    }
834
835  fputs ("\
836#\n\
837# This shar contains:\n\
838# length mode       name\n\
839# ------ ---------- ------------------------------------------\n",
840	 output);
841
842  for (counter = 0; counter < argc; counter++)
843    {
844
845      /* Output names of files but not parameters.  */
846
847      if (intermixed_parameter_mode &&
848	  (strcmp (argv[counter], "-B") == 0 ||
849	   strcmp (argv[counter], "-T") == 0 ||
850	   strcmp (argv[counter], "-M") == 0 ||
851	   strcmp (argv[counter], "-z") == 0 ||
852	   strcmp (argv[counter], "-Z") == 0 ||
853	   strcmp (argv[counter], "-C") == 0))
854	continue;
855
856      if (walktree (generate_one_header_line, argv[counter]))
857	exit (EXIT_FAILURE);
858    }
859  fputs ("#\n", output);
860
861  generate_configure ();
862
863  if (split_file_mode)
864    {
865
866      /* Now check the sequence.  */
867
868      fprintf (output, "\
869if test -r _sh%05d/seq; then\n\
870  $echo '%s'\n\
871  $echo '%s' '`cat _sh%05d/seq`' '%s'\n\
872  exit 1\n\
873fi\n",
874	       sharpid,
875	       N_("Must unpack archives in sequence!"),
876	       N_("Please unpack part"), sharpid, N_("next!")
877);
878    }
879}
880
881/* Prepare a shar script.  */
882
883/*---.
884| ?  |
885`---*/
886
887static int
888shar (local_name, restore_name)
889     const char *local_name;
890     const char *restore_name;
891{
892  char buffer[BUFSIZ];
893  FILE *input;
894  long current_size;
895  long remaining_size;
896  int split_flag = 0;		/* file split flag */
897  const char *file_type;	/* text of binary */
898  const char *file_type_remote;	/* text or binary, avoiding locale */
899  struct tm *restore_time;
900
901  /* Check to see that this is still a regular file and readable.  */
902
903  if (!S_ISREG (struct_stat.st_mode & S_IFMT))
904    {
905      error (0, 0, _("%s: Not a regular file"), local_name);
906      return 1;
907    }
908  if (access (local_name, 4))
909    {
910      error (0, 0, _("Cannot access %s"), local_name);
911      return 1;
912    }
913
914  /* If file_size_limit set, get the current output length.  */
915
916  if (file_size_limit)
917    {
918      current_size = ftell (output);
919      remaining_size = (file_size_limit * 1024L) - current_size;
920      DEBUG_PRINT (_("In shar: remaining size %ld\n"), remaining_size);
921
922      if (!split_file_mode && current_size > first_file_position
923	  && ((uuencoded_file_mode
924	       ? struct_stat.st_size + struct_stat.st_size / 3
925	       : struct_stat.st_size)
926	      > remaining_size))
927	{
928
929	  /* Change to another file.  */
930
931	  DEBUG_PRINT (_("Newfile, remaining %ld, "), remaining_size);
932	  DEBUG_PRINT (_("Limit still %d\n"), file_size_limit);
933
934	  /* Close the "&&" and report an error if any of the above
935	     failed.  */
936
937	  /* The following code is somewhat unreadable because marking
938	     the strings is necessary.  The complete string is (nice
939	     formatted:
940: || $echo 'restore of %s failed'\n\
941echo 'End of part %d, continue with part %d'\n\
942exit 0\n",
943           */
944	  fprintf (output, "\
945: || $echo '%s' '%s' '%s'\n\
946$echo '%s' '%d,' '%s' '%d'\n\
947exit 0\n",
948		   N_("restore of"), restore_name, N_("failed"),
949		   N_("End of part"), part_number,
950		   N_("continue with part"), part_number + 1);
951
952	  close_output ();
953
954	  /* Clear mkdir_already in case the user unshars out of order.  */
955
956	  while (mkdir_already_count > 0)
957	    free (mkdir_already[--mkdir_already_count]);
958
959	  /* Form the next filename.  */
960
961	  open_output ();
962	  if (!quiet_mode)
963	    fprintf (stderr, _("Starting file %s\n"), output_filename);
964
965	  if (net_headers_mode)
966	    {
967	      fprintf (output, "Submitted-by: %s\n", submitter_address);
968	      fprintf (output, "Archive-name: %s%s%02d\n\n", archive_name,
969		       strchr (archive_name, '/') ? "" : "/part",
970		       part_number ? part_number : 1);
971	    }
972
973	  if (cut_mark_mode)
974	    fputs (cut_mark_line, output);
975
976	  fprintf (output, "\
977#!/bin/sh\n\
978# This is part %02d of %s.\n",
979		   part_number,
980		   archive_name ? archive_name : "a multipart archive");
981
982	  generate_configure ();
983
984	  first_file_position = ftell (output);
985	}
986    }
987  else
988    remaining_size = 0;		/* give some value to the variable */
989
990  fprintf (output, "\
991# ============= %s ==============\n",
992	   restore_name);
993
994  generate_mkdir_script (restore_name);
995
996  if (struct_stat.st_size == 0)
997    {
998      file_type = _("empty");
999      file_type_remote = N_("(empty)");
1000      input = NULL;		/* give some value to the variable */
1001    }
1002  else
1003    {
1004
1005      /* If mixed, determine the file type.  */
1006
1007      if (mixed_uuencoded_file_mode)
1008	{
1009
1010	  /* Uuencoded was once decided through calling the `file'
1011	     program and studying its output: the method was slow and
1012	     error prone.  There is only one way of doing it correctly,
1013	     and this is to read the input file, seeking for one binary
1014	     character.  Considering the average file size, even reading
1015	     the whole file (if it is text) would be usually faster than
1016	     calling `file'.  */
1017
1018	  int character;
1019	  int line_length;
1020
1021	  if (input = fopen (local_name, "rb"), input == NULL)
1022	    {
1023	      error (0, errno, _("Cannot open file %s"), local_name);
1024	      return 1;
1025	    }
1026
1027	  /* Assume initially that the input file is text.  Then try to prove
1028	     it is binary by looking for binary characters or long lines.  */
1029
1030	  uuencoded_file_mode = 0;
1031	  line_length = 0;
1032	  while (character = getc (input), character != EOF)
1033	    if (character == '\n')
1034	      line_length = 0;
1035	    else if (
1036#ifdef __CHAR_UNSIGNED__
1037		     byte_is_binary[character]
1038#else
1039		     byte_is_binary[character & 0xFF]
1040#endif
1041		     || line_length == MAXIMUM_NON_BINARY_LINE)
1042	      {
1043		uuencoded_file_mode = 1;
1044		break;
1045	      }
1046	    else
1047	      line_length++;
1048	  fclose (input);
1049
1050	  /* Text files should terminate by an end of line.  */
1051
1052	  if (line_length > 0)
1053	    uuencoded_file_mode = 1;
1054	}
1055
1056      if (uuencoded_file_mode)
1057	{
1058	  static int pid, pipex[2];
1059
1060	  file_type = (compressed_file_mode ? _("compressed")
1061		       : gzipped_file_mode ? _("gzipped") : _("binary"));
1062	  file_type_remote = (compressed_file_mode ? N_("(compressed)")
1063			      : gzipped_file_mode ? N_("(gzipped)")
1064			        : N_("(binary)"));
1065
1066	  /* Fork a uuencode process.  */
1067
1068	  pipe (pipex);
1069	  fflush (output);
1070
1071	  if (pid = fork (), pid != 0)
1072	    {
1073
1074	      /* Parent, create a file to read.  */
1075
1076	      if (pid < 0)
1077		error (EXIT_FAILURE, errno, _("Could not fork"));
1078	      close (pipex[1]);
1079	      input = fdopen (pipex[0], "r");
1080	      if (!input)
1081		{
1082		  error (0, errno, _("File %s (%s)"), local_name, file_type);
1083		  return 1;
1084		}
1085	    }
1086	  else
1087	    {
1088
1089	      /* Start writing the pipe with encodes.  */
1090
1091	      FILE *outptr;
1092
1093	      if (compressed_file_mode)
1094		{
1095		  sprintf (buffer, "compress -b%d < '%s'",
1096			   bits_per_compressed_byte, local_name);
1097		  input = popen (buffer, "r");
1098		}
1099	      else if (gzipped_file_mode)
1100		{
1101		  sprintf (buffer, "gzip -%d < '%s'",
1102			   gzip_compression_level, local_name);
1103		  input = popen (buffer, "r");
1104		}
1105	      else
1106		input = fopen (local_name, "rb");
1107
1108	      outptr = fdopen (pipex[1], "w");
1109	      fputs ("begin 600 ", outptr);
1110	      if (compressed_file_mode)
1111		fprintf (outptr, "_sh%05d/cmp\n", sharpid);
1112	      else if (gzipped_file_mode)
1113		fprintf (outptr, "_sh%05d/gzi\n", sharpid);
1114	      else
1115		fprintf (outptr, "%s\n", restore_name);
1116	      copy_file_encoded (input, outptr);
1117	      fprintf (outptr, "end\n");
1118	      if (compressed_file_mode || gzipped_file_mode)
1119		pclose (input);
1120	      else
1121		fclose (input);
1122
1123	      exit (EXIT_SUCCESS);
1124	    }
1125	}
1126      else
1127	{
1128	  file_type = _("text");
1129	  file_type_remote = N_("(text)");
1130
1131	  input = fopen (local_name, "r");
1132	  if (!input)
1133	    {
1134	      error (0, errno, _("File %s (%s)"), local_name, file_type);
1135	      return 1;
1136	    }
1137	}
1138    }
1139
1140  /* Protect existing files.  */
1141
1142  if (check_existing_mode)
1143    {
1144      fprintf (output, "\
1145if test -f '%s' && test \"$first_param\" != -c; then\n",
1146	       restore_name);
1147
1148      if (query_user_mode)
1149	{
1150	  fprintf (output, "\
1151  case $shar_wish in\n\
1152    A*|a*)\n\
1153      $echo 'x -' %s '%s' ;;\n\
1154    *)\n\
1155      $echo $shar_n '? -' %s '%s' '%s' $shar_c\n\
1156      if test -n \"$shar_tty\"; then\n\
1157	read shar_wish < $shar_tty\n\
1158      else\n\
1159	read shar_wish\n\
1160      fi ;;\n\
1161  esac\n\
1162  case $shar_wish in\n\
1163    Q*|q*)\n\
1164      $echo '%s'; exit 1 ;;\n\
1165    A*|a*|Y*|y*)\n\
1166      shar_skip=no ;;\n\
1167    *)\n\
1168      shar_skip=yes ;;\n\
1169  esac\n\
1170else\n\
1171  shar_skip=no\n\
1172fi\n\
1173if test $shar_skip = yes; then\n\
1174  $echo 'x -' %s '%s'\n",
1175		   N_("overwriting"), restore_name,
1176		   N_("overwrite"), restore_name,
1177		   N_("[no, yes, all, quit] (no)?"),
1178		   N_("extraction aborted"),
1179		   N_("SKIPPING"), restore_name);
1180	}
1181      else
1182	fprintf (output, "\
1183  $echo 'x -' %s '%s' '%s'\n",
1184		 N_("SKIPPING"), restore_name, N_("(file already exists)"));
1185
1186      if (split_file_mode)
1187	fprintf (output, "\
1188  rm -f _sh%05d/new\n",
1189		 sharpid);
1190
1191      fputs ("\
1192else\n",
1193	     output);
1194
1195      if (split_file_mode)
1196	fprintf (output, "\
1197  > _sh%05d/new\n",
1198		 sharpid);
1199    }
1200
1201  if (!quiet_mode)
1202    error (0, 0, _("Saving %s (%s)"), local_name, file_type);
1203
1204  if (!quiet_unshar_mode)
1205    fprintf (output, "\
1206  $echo 'x -' %s '%s' '%s'\n",
1207	     N_("extracting"), restore_name, file_type_remote);
1208
1209  if (struct_stat.st_size == 0)
1210    {
1211
1212      /* Just touch the file, or empty it if it exists.  */
1213
1214      fprintf (output, "\
1215  > '%s' &&\n",
1216	       restore_name);
1217    }
1218  else
1219    {
1220
1221      /* Run sed for non-empty files.  */
1222
1223      if (uuencoded_file_mode)
1224	{
1225
1226	  /* Run sed through uudecode (via temp file if might get split).  */
1227
1228	  fprintf (output, "\
1229  sed 's/^%c//' << '%s' ",
1230		   line_prefix, here_delimiter);
1231	  if (inhibit_piping_mode)
1232	    fprintf (output, "> _sh%05d/uue &&\n", sharpid);
1233	  else
1234	    fputs ("| uudecode &&\n", output);
1235	}
1236      else
1237	{
1238
1239	  /* Just run it into the file.  */
1240
1241	  fprintf (output, "\
1242  sed 's/^%c//' << '%s' > '%s' &&\n",
1243		   line_prefix, here_delimiter, restore_name);
1244	}
1245
1246      while (fgets (buffer, BUFSIZ, input))
1247	{
1248
1249	  /* Output a line and test the length.  */
1250
1251	  if (!mandatory_prefix_mode
1252	      && ISASCII (buffer[0])
1253#ifdef isgraph
1254	      && isgraph (buffer[0])
1255#else
1256	      && isprint (buffer[0]) && !isspace (buffer[0])
1257#endif
1258	      /* Protect lines already starting with the prefix.  */
1259	      && buffer[0] != line_prefix
1260
1261	      /* Old mail programs interpret ~ directives.  */
1262	      && buffer[0] != '~'
1263
1264	      /* Avoid mailing lines which are just `.'.  */
1265	      && buffer[0] != '.'
1266
1267#if STRNCMP_IS_FAST
1268	      && strncmp (buffer, here_delimiter, here_delimiter_length)
1269
1270	      /* unshar -e: avoid `exit 0'.  */
1271	      && strncmp (buffer, "exit 0", 6)
1272
1273	      /* Don't let mail prepend a `>'.  */
1274	      && strncmp (buffer, "From", 4)
1275#else
1276	      && (buffer[0] != here_delimiter[0]
1277		  || strncmp (buffer, here_delimiter, here_delimiter_length))
1278
1279	      /* unshar -e: avoid `exit 0'.  */
1280	      && (buffer[0] != 'e' || strncmp (buffer, "exit 0", 6))
1281
1282	      /* Don't let mail prepend a `>'.  */
1283	      && (buffer[0] != 'F' || strncmp (buffer, "From", 4))
1284#endif
1285	      )
1286	    fputs (buffer, output);
1287	  else
1288	    {
1289	      fprintf (output, "%c%s", line_prefix, buffer);
1290	      remaining_size--;
1291	    }
1292
1293	  /* Try completing an incomplete line, but not if the incomplete
1294	     line contains no character.  This might occur with -T for
1295	     incomplete files, or sometimes when switching to a new file.  */
1296
1297	  if (*buffer && buffer[strlen (buffer) - 1] != '\n')
1298	    {
1299	      putc ('\n', output);
1300	      remaining_size--;
1301	    }
1302
1303	  if (split_file_mode
1304#if MSDOS
1305	      /* 1 extra for CR.  */
1306	      && (remaining_size -= (int) strlen (buffer) + 1) < 0
1307#else
1308	      && (remaining_size -= (int) strlen (buffer)) < 0
1309#endif
1310	      )
1311	    {
1312
1313	      /* Change to another file.  */
1314
1315	      DEBUG_PRINT (_("Newfile, remaining %ld, "), remaining_size);
1316	      DEBUG_PRINT (_("Limit still %d\n"), file_size_limit);
1317
1318	      fprintf (output, "%s\n", here_delimiter);
1319
1320	      /* Close the "&&" and report an error if any of the above
1321		 failed.  */
1322
1323	      fprintf (output, "\
1324  : || $echo '%s' '%s' '%s'\n",
1325		       N_("restore of"), restore_name, N_("failed"));
1326
1327	      if (check_existing_mode)
1328		fputs ("\
1329fi\n",
1330		       output);
1331
1332	      if (quiet_unshar_mode)
1333		fprintf (output, "\
1334$echo '%s' '%d,' '%s' '%d'\n",
1335			 N_("End of part"), part_number,
1336			 N_("continue with part"), part_number + 1);
1337	      else
1338		fprintf (output, "\
1339$echo '%s' '%s' '%s' '%d'\n\
1340$echo '%s' '%s' '%s' '%d'\n",
1341			 N_("End of"),
1342			 archive_name ? archive_name : N_("archive"),
1343			 N_("part"),
1344			 part_number,
1345			 N_("File"), restore_name,
1346			 N_("is continued in part"), part_number + 1);
1347
1348	      fprintf (output, "\
1349echo %d > _sh%05d/seq\n\
1350exit 0\n",
1351		       part_number + 1, sharpid);
1352
1353	      if (part_number == 1)
1354		{
1355
1356		  /* Rewrite the info lines on the first header.  */
1357
1358		  fseek (output, archive_type_position, 0);
1359		  fprintf (output, "%-75s\n%-75s\n",
1360			   "\
1361# This is part 1 of a multipart archive.",
1362			   "\
1363# Do not concatenate these parts, unpack them in order with `/bin/sh'.");
1364			   }
1365	      close_output ();
1366
1367	      /* Next! */
1368
1369	      open_output ();
1370
1371	      if (net_headers_mode)
1372		{
1373		  fprintf (output, "Submitted-by: %s\n", submitter_address);
1374		  fprintf (output, "Archive-name: %s%s%02d\n\n",
1375			   archive_name,
1376			   strchr (archive_name, '/') ? "" : "/part",
1377			   part_number ? part_number : 1);
1378		}
1379
1380	      if (cut_mark_mode)
1381		fputs (cut_mark_line, output);
1382
1383	      fprintf (output, "\
1384#!/bin/sh\n\
1385# This is `%s' (part %d of %s).\n\
1386# Do not concatenate these parts, unpack them in order with `/bin/sh'.\n\
1387# File `%s' is being continued...\n\
1388#\n",
1389		       basename (output_filename), part_number,
1390		       archive_name ? archive_name : "a multipart archive",
1391		       restore_name);
1392
1393	      generate_configure ();
1394
1395	      fprintf (output, "\
1396if test ! -r _sh%05d/seq; then\n\
1397  $echo '%s'\n\
1398  exit 1\n\
1399fi\n\
1400shar_sequence=`cat _sh%05d/seq`\n\
1401if test \"$shar_sequence\" != %d; then\n\
1402  $echo '%s' \"$shar_sequence\" '%s'\n\
1403  exit 1\n\
1404fi\n",
1405		       sharpid,
1406		       N_("Please unpack part 1 first!"),
1407		       sharpid,
1408		       part_number,
1409		       N_("Please unpack part"),
1410		       N_("next!"));
1411
1412	      if (check_existing_mode)
1413		if (quiet_unshar_mode)
1414		  fprintf (output, "\
1415if test -f _sh%05d/new; then\n",
1416			   sharpid);
1417		else
1418		  fprintf (output, "\
1419if test ! -f _sh%05d/new; then\n\
1420  $echo 'x -' '%s' '%s'\n\
1421else\n",
1422			   sharpid,
1423			   N_("STILL SKIPPING"), restore_name);
1424
1425	      if (!quiet_mode)
1426		fprintf (stderr, _("Starting file %s\n"), output_filename);
1427	      if (!quiet_unshar_mode)
1428		fprintf (output, "\
1429  $echo 'x -' '%s' '%s'\n",
1430			 N_("continuing file"), restore_name);
1431	      fprintf (output, "\
1432  sed 's/^%c//' << '%s' >> ",
1433		       line_prefix, here_delimiter);
1434	      if (uuencoded_file_mode)
1435		fprintf (output, "_sh%05d/uue &&\n", sharpid);
1436	      else
1437		fprintf (output, "%s &&\n", restore_name);
1438	      remaining_size = file_size_limit * 1024L;
1439	      split_flag = 1;
1440	    }
1441	}
1442
1443      fclose (input);
1444      while (wait (NULL) >= 0)
1445	;
1446
1447      fprintf (output, "%s\n", here_delimiter);
1448      if (split_flag && !quiet_unshar_mode)
1449	fprintf (output, "\
1450  $echo '%s' '%s' '%s' &&\n",
1451		 N_("File"), restore_name, N_("is complete"));
1452
1453      /* If this file was uuencoded w/Split, decode it and drop the temp.  */
1454
1455      if (uuencoded_file_mode && inhibit_piping_mode)
1456	{
1457	  if (!quiet_unshar_mode)
1458	    fprintf (output, "\
1459  $echo '%s' '%s' &&\n",
1460		     N_("uudecoding file"), restore_name);
1461
1462	  fprintf (output, "\
1463  uudecode _sh%05d/uue < _sh%05d/uue &&\n",
1464		   sharpid, sharpid);
1465	}
1466
1467      /* If this file was compressed, uncompress it and drop the temp.  */
1468
1469      if (compressed_file_mode)
1470	{
1471	  if (!quiet_unshar_mode)
1472	    fprintf (output, "\
1473  $echo '%s' '%s' &&\n",
1474		     N_("uncompressing file"), restore_name);
1475
1476	  fprintf (output, "\
1477  compress -d < _sh%05d/cmp > '%s' &&\n",
1478		   sharpid, restore_name);
1479	}
1480      else if (gzipped_file_mode)
1481	{
1482	  if (!quiet_unshar_mode)
1483	    fprintf (output, "\
1484  $echo '%s' '%s' &&\n",
1485		     N_("gunzipping file"), restore_name);
1486
1487	  fprintf (output, "\
1488  gzip -d < _sh%05d/gzi > '%s' &&\n",
1489		   sharpid, restore_name);
1490	}
1491    }
1492
1493  if (timestamp_mode)
1494    {
1495
1496      /* Set the dates as they were.  */
1497
1498      restore_time = localtime (&struct_stat.st_mtime);
1499      fprintf (output, "\
1500  (set %02d %02d %02d %02d %02d %02d %02d '%s'; eval \"$shar_touch\") &&\n",
1501	       (restore_time->tm_year + 1900) / 100,
1502	       (restore_time->tm_year + 1900) % 100,
1503	       restore_time->tm_mon + 1, restore_time->tm_mday,
1504	       restore_time->tm_hour, restore_time->tm_min,
1505	       restore_time->tm_sec, restore_name);
1506    }
1507
1508  if (vanilla_operation_mode)
1509    {
1510
1511      /* Close the "&&" and report an error if any of the above
1512	 failed.  */
1513
1514      fprintf (output, "\
1515  : || $echo '%s' '%s' '%s'\n",
1516	       N_("restore of"), restore_name, N_("failed"));
1517    }
1518  else
1519    {
1520      unsigned char md5buffer[16];
1521      FILE *fp = NULL;
1522      int did_md5 = 0;
1523
1524      /* Set the permissions as they were.  */
1525
1526      fprintf (output, "\
1527  chmod %04o '%s' ||\n",
1528	       (unsigned) (struct_stat.st_mode & 0777), restore_name);
1529
1530      /* Report an error if any of the above failed.  */
1531
1532      fprintf (output, "\
1533  $echo '%s' '%s' '%s'\n",
1534	       N_("restore of"), restore_name, N_("failed"));
1535
1536      if (md5_count_mode && (fp = fopen (local_name, "r")) != NULL
1537	  && md5_stream (fp, md5buffer) == 0)
1538	{
1539	  /* Validate the transferred file using `md5sum' command.  */
1540	  size_t cnt;
1541	  did_md5 = 1;
1542
1543	  fprintf (output, "\
1544  if ( %s --help 2>&1 | grep 'sage: md5sum \\[' ) >/dev/null 2>&1 \\\n\
1545  && ( %s --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then\n\
1546    %s -c << %s >/dev/null 2>&1 \\\n\
1547    || $echo '%s:' '%s'\n",
1548		   MD5SUM_COMMAND, MD5SUM_COMMAND, MD5SUM_COMMAND,
1549		   DEFAULT_HERE_DELIMITER, local_name, N_("MD5 check failed"));
1550
1551	  for (cnt = 0; cnt < 16; ++cnt)
1552	    fprintf (output, "%02x", md5buffer[cnt]);
1553
1554	  fprintf (output, " %c%s\n\
1555%s\n",
1556		   ' ', local_name, DEFAULT_HERE_DELIMITER);
1557	  /* This  ^^^ space is not necessarily a parameter now.  But it
1558	     is a flag for binary/text mode and will perhaps be used later.  */
1559	}
1560
1561      if (fp != NULL)
1562	fclose (fp);
1563
1564      if (character_count_mode)
1565	{
1566	  /* Validate the transferred file using simple `wc' command.  */
1567
1568	  FILE *pfp;
1569	  char command[BUFSIZ];
1570
1571	  sprintf (command, "%s '%s'", CHARACTER_COUNT_COMMAND, local_name);
1572	  if (pfp = popen (command, "r"), pfp)
1573	    {
1574	      char wc[BUFSIZ];
1575	      const char *prefix = "";
1576
1577	      if (did_md5)
1578		{
1579		  fputs ("  else\n", output);
1580		  prefix = "  ";
1581		}
1582
1583	      fscanf (pfp, "%s", wc);
1584	      fprintf (output, "\
1585%s  shar_count=\"`%s '%s'`\"\n\
1586%s  test %s -eq \"$shar_count\" ||\n\
1587%s  $echo '%s:' '%s' '%s,' '%s' \"$shar_count!\"\n",
1588		       prefix, CHARACTER_COUNT_COMMAND, restore_name,
1589		       prefix, wc,
1590		       prefix, restore_name, N_("original size"), wc,
1591		       N_("current size"));
1592	      pclose (pfp);
1593	    }
1594	}
1595      if (did_md5)
1596	fputs ("  fi\n", output);
1597    }
1598
1599  /* If the exists option is in place close the if.  */
1600
1601  if (check_existing_mode)
1602    fputs ("\
1603fi\n",
1604	   output);
1605
1606  return 0;
1607}
1608
1609/* Main control.  */
1610
1611/*-----------------------------------------------------------------------.
1612| Set file mode, accepting a parameter 'M' for mixed uuencoded mode, 'B' |
1613| for uuencoded mode, 'z' for gzipped mode or 'Z' for compressed mode.	 |
1614| Any other value yields text mode.					 |
1615`-----------------------------------------------------------------------*/
1616
1617static void
1618set_file_mode (mode)
1619     int mode;
1620{
1621  mixed_uuencoded_file_mode = mode == 'M';
1622  uuencoded_file_mode = mode == 'B';
1623  gzipped_file_mode = mode == 'z';
1624  compressed_file_mode = mode == 'Z';
1625
1626  if (gzipped_file_mode || compressed_file_mode)
1627    uuencoded_file_mode = 1;
1628}
1629
1630/*-------------------------------------------.
1631| Open the next output file, or die trying.  |
1632`-------------------------------------------*/
1633
1634static void
1635open_output ()
1636{
1637  sprintf (output_filename, output_base_name, ++part_number);
1638  output = fopen (output_filename, "w");
1639  if (!output)
1640    error (EXIT_FAILURE, errno, _("Opening `%s'"), output_filename);
1641}
1642
1643/*-----------------------------------------------.
1644| Close the current output file, or die trying.	 |
1645`-----------------------------------------------*/
1646
1647static void
1648close_output ()
1649{
1650  if (fclose (output) != 0)
1651    error (EXIT_FAILURE, errno, _("Closing `%s'"), output_filename);
1652}
1653
1654/*----------------------------------.
1655| Output a command format message.  |
1656`----------------------------------*/
1657
1658static void
1659usage (status)
1660     int status;
1661{
1662  if (status != EXIT_SUCCESS)
1663    fprintf (stderr, _("Try `%s --help' for more information.\n"),
1664	     program_name);
1665  else
1666    {
1667      printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
1668      fputs (_("\
1669Mandatory arguments to long options are mandatory for short options too.\n"),
1670	     stdout);
1671      fputs (_("\
1672\n\
1673Giving feedback:\n\
1674      --help              display this help and exit\n\
1675      --version           output version information and exit\n\
1676  -q, --quiet, --silent   do not output verbose messages locally\n\
1677\n\
1678Selecting files:\n\
1679  -p, --intermix-type     allow -[BTzZ] in file lists to change mode\n\
1680  -S, --stdin-file-list   read file list from standard input\n\
1681\n\
1682Splitting output:\n\
1683  -o, --output-prefix=PREFIX    output to file PREFIX.01 through PREFIX.NN\n\
1684  -l, --whole-size-limit=SIZE   split archive, not files, to SIZE kilobytes\n\
1685  -L, --split-size-limit=SIZE   split archive, or files, to SIZE kilobytes\n"),
1686	     stdout);
1687      fputs (_("\
1688\n\
1689Controlling the shar headers:\n\
1690  -n, --archive-name=NAME   use NAME to document the archive\n\
1691  -s, --submitter=ADDRESS   override the submitter name\n\
1692  -a, --net-headers         output Submitted-by: & Archive-name: headers\n\
1693  -c, --cut-mark            start the shar with a cut line\n\
1694\n\
1695Selecting how files are stocked:\n\
1696  -M, --mixed-uuencode         dynamically decide uuencoding (default)\n\
1697  -T, --text-files             treat all files as text\n\
1698  -B, --uuencode               treat all files as binary, use uuencode\n\
1699  -z, --gzip                   gzip and uuencode all files\n\
1700  -g, --level-for-gzip=LEVEL   pass -LEVEL (default 9) to gzip\n\
1701  -Z, --compress               compress and uuencode all files\n\
1702  -b, --bits-per-code=BITS     pass -bBITS (default 12) to compress\n"),
1703	     stdout);
1704      fputs (_("\
1705\n\
1706Protecting against transmission:\n\
1707  -w, --no-character-count      do not use `wc -c' to check size\n\
1708  -D, --no-md5-digest           do not use `md5sum' digest to verify\n\
1709  -F, --force-prefix            force the prefix character on every line\n\
1710  -d, --here-delimiter=STRING   use STRING to delimit the files in the shar\n\
1711\n\
1712Producing different kinds of shars:\n\
1713  -V, --vanilla-operation   produce very simple and undemanding shars\n\
1714  -P, --no-piping           exclusively use temporary files at unshar time\n\
1715  -x, --no-check-existing   blindly overwrite existing files\n\
1716  -X, --query-user          ask user before overwriting files (not for Net)\n\
1717  -m, --no-timestamp        do not restore file modification dates & times\n\
1718  -Q, --quiet-unshar        avoid verbose messages at unshar time\n\
1719  -f, --basename            restore in one directory, despite hierarchy\n\
1720      --no-i18n             do not produce internationalized shell script\n"),
1721	     stdout);
1722      fputs (_("\
1723\n\
1724Option -o is required with -l or -L, option -n is required with -a.\n\
1725Option -g implies -z, option -b implies -Z.\n"),
1726	     stdout);
1727    }
1728  exit (status);
1729}
1730
1731/*--------------------------------------.
1732| Decode options and launch execution.  |
1733`--------------------------------------*/
1734
1735static const struct option long_options[] =
1736{
1737  {"archive-name", required_argument, NULL, 'n'},
1738  {"basename", no_argument, NULL, 'f'},
1739  {"bits-per-code", required_argument, NULL, 'b'},
1740  {"compress", no_argument, NULL, 'Z'},
1741  {"cut-mark", no_argument, NULL, 'c'},
1742  {"force-prefix", no_argument, NULL, 'F'},
1743  {"gzip", no_argument, NULL, 'z'},
1744  {"here-delimiter", required_argument, NULL, 'd'},
1745  {"intermix-type", no_argument, NULL, 'p'},
1746  {"level-for-gzip", required_argument, NULL, 'g'},
1747  {"mixed-uuencode", no_argument, NULL, 'M'},
1748  {"net-headers", no_argument, NULL, 'a'},
1749  {"no-character-count", no_argument, &character_count_mode, 0},
1750  {"no-check-existing", no_argument, NULL, 'x'},
1751  {"no-i18n", no_argument, &no_i18n, 1},
1752  {"no-md5-digest", no_argument, &md5_count_mode, 0},
1753  {"no-piping", no_argument, NULL, 'P'},
1754  {"no-timestamp", no_argument, NULL, 'm'},
1755  {"output-prefix", required_argument, NULL, 'o'},
1756  {"print-text-domain-dir", no_argument, &print_text_dom_dir, 1},
1757  {"query-user", no_argument, NULL, 'X'},
1758  {"quiet", no_argument, NULL, 'q'},
1759  {"quiet-unshar", no_argument, NULL, 'Q'},
1760  {"split-size-limit", required_argument, NULL, 'L'},
1761  {"stdin-file-list", no_argument, NULL, 'S'},
1762  {"submitter", required_argument, NULL, 's'},
1763  {"text-files", no_argument, NULL, 'T'},
1764  {"uuencode", no_argument, NULL, 'B'},
1765  {"vanilla-operation", no_argument, NULL, 'V'},
1766  {"whole-size-limit", required_argument, NULL, 'l'},
1767
1768  {"help", no_argument, &show_help, 1},
1769  {"version", no_argument, &show_version, 1},
1770
1771  { NULL, 0, NULL, 0 },
1772};
1773
1774/*---.
1775| ?  |
1776`---*/
1777
1778int
1779main (argc, argv)
1780     int argc;
1781     char *const *argv;
1782{
1783  int status = EXIT_SUCCESS;
1784  int stdin_file_list = 0;
1785  int optchar;
1786
1787  program_name = argv[0];
1788  sharpid = (int) getpid ();
1789  setlocale (LC_ALL, "");
1790
1791  /* Set the text message domain.  */
1792  bindtextdomain (PACKAGE, LOCALEDIR);
1793  textdomain (PACKAGE);
1794
1795  while (optchar = getopt_long (argc, argv,
1796				"+$BCDFL:MPQSTVXZab:cd:fg:hl:mn:o:pqs:wxz",
1797				long_options, NULL),
1798	 optchar != EOF)
1799    switch (optchar)
1800      {
1801      case '\0':
1802	break;
1803
1804      case '$':
1805#if DEBUG
1806	debugging_mode = 1;
1807#else
1808	error (0, 0, _("DEBUG was not selected at compile time"));
1809#endif
1810	break;
1811
1812      case 'B':
1813	set_file_mode ('B');
1814	break;
1815
1816      case 'D':
1817	md5_count_mode = 0;
1818	break;
1819
1820      case 'F':
1821	mandatory_prefix_mode = 1;
1822	break;
1823
1824      case 'L':
1825	if (file_size_limit = atoi (optarg), file_size_limit > 1)
1826	  file_size_limit--;
1827	split_file_mode = file_size_limit != 0;
1828	inhibit_piping_mode = 1;
1829	DEBUG_PRINT (_("Hard limit %dk\n"), file_size_limit);
1830	break;
1831
1832      case 'M':
1833	set_file_mode ('M');
1834	break;
1835
1836      case 'P':
1837	inhibit_piping_mode = 1;
1838	break;
1839
1840      case 'Q':
1841	quiet_unshar_mode = 1;
1842	break;
1843
1844      case 'S':
1845	stdin_file_list = 1;
1846	break;
1847
1848      case 'V':
1849	vanilla_operation_mode = 1;
1850	break;
1851
1852      case 'T':
1853	set_file_mode ('T');
1854	break;
1855
1856      case 'X':
1857	query_user_mode = 1;
1858	check_existing_mode = 1;
1859	break;
1860
1861      case 'b':
1862	bits_per_compressed_byte = atoi (optarg);
1863	/* Fall through.  */
1864
1865      case 'C':
1866      case 'Z':
1867	if (optchar == 'C')
1868	  error (0, 0, _("-C is being deprecated, use -Z instead"));
1869	set_file_mode ('Z');
1870	break;
1871
1872      case 'a':
1873	net_headers_mode = 1;
1874	break;
1875
1876      case 'c':
1877	cut_mark_mode = 1;
1878	break;
1879
1880      case 'd':
1881	here_delimiter = optarg;
1882	break;
1883
1884      case 'f':
1885	basename_mode = 1;
1886	break;
1887
1888      case 'h':
1889	usage (EXIT_SUCCESS);
1890	break;
1891
1892      case 'l':
1893	if (file_size_limit = atoi (optarg), file_size_limit > 1)
1894	  file_size_limit--;
1895	split_file_mode = 0;
1896	DEBUG_PRINT (_("Soft limit %dk\n"), file_size_limit);
1897	break;
1898
1899      case 'm':
1900	timestamp_mode = 0;
1901	break;
1902
1903      case 'n':
1904	archive_name = optarg;
1905	break;
1906
1907      case 'o':
1908	strcpy (output_base_name, optarg);
1909	if (!strchr (output_base_name, '%'))
1910	  strcat (output_base_name, ".%02d");
1911	part_number = 0;
1912	open_output ();
1913	break;
1914
1915      case 'p':
1916	intermixed_parameter_mode = 1;
1917	break;
1918
1919      case 'q':
1920	quiet_mode = 1;
1921	break;
1922
1923      case 's':
1924	submitter_address = optarg;
1925	break;
1926
1927      case 'w':
1928	character_count_mode = 0;
1929	break;
1930
1931      case 'x':
1932	check_existing_mode = 0;
1933	break;
1934
1935      case 'g':
1936	gzip_compression_level = atoi (optarg);
1937	/* Fall through.  */
1938
1939      case 'z':
1940	set_file_mode ('z');
1941	break;
1942
1943      default:
1944	usage (EXIT_FAILURE);
1945      }
1946
1947  /* Internationalized shell scripts are not vanilla.  */
1948  if (vanilla_operation_mode)
1949    no_i18n = 1;
1950
1951  if (show_version)
1952    {
1953      printf ("%s - GNU %s %s\n", program_name, PACKAGE, VERSION);
1954      exit (EXIT_SUCCESS);
1955    }
1956
1957  if (show_help)
1958    usage (EXIT_SUCCESS);
1959
1960  if (print_text_dom_dir != 0)
1961    {
1962      /* Support for internationalized shell scripts is only usable with
1963	 GNU gettext.  If we don't use it simply mark it as not available.  */
1964#if !defined ENABLE_NLS || defined HAVE_CATGETS \
1965    || (defined HAVE_GETTEXT && !defined __USE_GNU_GETTEXT)
1966      exit (EXIT_FAILURE);
1967#else
1968      extern const char _nl_default_dirname[]; /* Defined in dcgettext.c  */
1969      puts (_nl_default_dirname);
1970      exit (EXIT_SUCCESS);
1971#endif
1972    }
1973
1974  line_prefix = (here_delimiter[0] == DEFAULT_LINE_PREFIX_1
1975		 ? DEFAULT_LINE_PREFIX_2
1976		 : DEFAULT_LINE_PREFIX_1);
1977
1978  here_delimiter_length = strlen (here_delimiter);
1979
1980  if (vanilla_operation_mode)
1981    {
1982      if (mixed_uuencoded_file_mode < 0)
1983	set_file_mode ('T');
1984
1985      /* Implies -m, -w, -D, -F and -P.  */
1986
1987      timestamp_mode = 0;
1988      character_count_mode = 0;
1989      md5_count_mode = 0;
1990      mandatory_prefix_mode = 1;
1991      inhibit_piping_mode = 1;
1992
1993      /* Forbids -X.  */
1994
1995      if (query_user_mode)
1996	{
1997	  error (0, 0, _("WARNING: No user interaction in vanilla mode"));
1998	  query_user_mode = 0;
1999	}
2000
2001      /* Diagnose if not in -T state.  */
2002
2003      if (mixed_uuencoded_file_mode
2004	  || uuencoded_file_mode
2005	  || gzipped_file_mode
2006	  || compressed_file_mode
2007	  || intermixed_parameter_mode)
2008	error (0, 0, _("WARNING: Non-text storage options overridden"));
2009    }
2010
2011  /* Set defaults for unset options.  */
2012
2013  if (mixed_uuencoded_file_mode < 0)
2014    set_file_mode ('M');
2015
2016  if (!submitter_address)
2017    submitter_address = get_submitter (NULL);
2018
2019  if (!output)
2020    output = stdout;
2021
2022  /* Maybe prepare to decide dynamically about file type.  */
2023
2024  if (mixed_uuencoded_file_mode || intermixed_parameter_mode)
2025    {
2026      memset ((char *) byte_is_binary, 1, 256);
2027      byte_is_binary['\b'] = 0;
2028      byte_is_binary['\t'] = 0;
2029      byte_is_binary['\f'] = 0;
2030      memset ((char *) byte_is_binary + 32, 0, 127 - 32);
2031    }
2032
2033  /* Maybe read file list from standard input.  */
2034
2035  if (stdin_file_list)
2036    {
2037      char stdin_buf[258];	/* FIXME: No fix limit in GNU... */
2038      char **list;
2039      int max_argc;
2040
2041      argc = 0;
2042      max_argc = 32;
2043      list = (char **) xmalloc (max_argc * sizeof (char *));
2044      stdin_buf[0] = 0;
2045      while (fgets (stdin_buf, sizeof (stdin_buf), stdin))
2046	{
2047	  if (argc == max_argc)
2048	    list = (char **) xrealloc (list,
2049				       (max_argc *= 2) * sizeof (char *));
2050	  if (stdin_buf[0] != '\0')
2051	    stdin_buf[strlen (stdin_buf) - 1] = 0;
2052	  list[argc] = xstrdup (stdin_buf);
2053	  ++argc;
2054	  stdin_buf[0] = 0;
2055	}
2056      argv = list;
2057      optind = 0;
2058    }
2059
2060  /* Diagnose various usage errors.  */
2061
2062  if (optind >= argc)
2063    {
2064      error (0, 0, _("No input files"));
2065      usage (EXIT_FAILURE);
2066    }
2067
2068  if (net_headers_mode && !archive_name)
2069    {
2070      error (0, 0, _("Cannot use -a option without -n"));
2071      usage (EXIT_FAILURE);
2072    }
2073
2074  if (file_size_limit && !part_number)
2075    {
2076      error (0, 0, _("Cannot use -l or -L option without -o"));
2077      usage (EXIT_FAILURE);
2078    }
2079
2080  /* Start making the archive file.  */
2081
2082  generate_full_header (argc - optind, &argv[optind]);
2083
2084  if (query_user_mode)
2085    {
2086      quiet_unshar_mode = 0;
2087      if (net_headers_mode)
2088	error (0, 0, _("PLEASE avoid -X shars on Usenet or public networks"));
2089
2090      fputs ("\
2091shar_wish=\n",
2092	     output);
2093    }
2094
2095  first_file_position = ftell (output);
2096
2097  /* Process positional parameters and files.  */
2098
2099  for (; optind < argc; optind++)
2100    if (intermixed_parameter_mode)
2101      if (strcmp (argv[optind], "-B") == 0)
2102	set_file_mode ('B');
2103      else if (strcmp (argv[optind], "-T") == 0)
2104	set_file_mode ('T');
2105      else if (strcmp (argv[optind], "-M") == 0)
2106	set_file_mode ('M');
2107      else if (strcmp (argv[optind], "-z") == 0)
2108	set_file_mode ('z');
2109      else if (strcmp (argv[optind], "-Z") == 0
2110	       || strcmp (argv[optind], "-C") == 0)
2111	set_file_mode ('Z');
2112      else
2113	{
2114	  if (walktree (shar, argv[optind]))
2115	    status = EXIT_FAILURE;
2116	}
2117    else
2118      {
2119	if (walktree (shar, argv[optind]))
2120	  status = EXIT_FAILURE;
2121      }
2122
2123  /* Delete the sequence file, if any.  */
2124
2125  if (split_file_mode && part_number > 1)
2126    {
2127      fprintf (output, "\
2128$echo '%s'\n",
2129	       N_("You have unpacked the last part"));
2130      if (quiet_mode)
2131	fprintf (stderr, _("Created %d files\n"), part_number);
2132    }
2133
2134  fprintf (output, "\
2135rm -fr _sh%05d\n\
2136exit 0\n",
2137	 sharpid);
2138
2139  exit (status);
2140}
2141