/*----------------------------------------------------------------------*/
/*		LHarc Archiver Driver for UNIX				*/
/*									*/
/*		Copyright(C) MCMLXXXIX  Yooichi.Tagawa			*/
/*		Thanks to H.Yoshizaki. (MS-DOS LHarc)			*/
/*									*/
/*  V0.00  Original				1988.05.23  Y.Tagawa	*/
/*  V0.01  Alpha Version (for 4.2BSD)		1989.05.28  Y.Tagawa	*/
/*  V0.02  Alpha Version Rel.2			1989.05.29  Y.Tagawa	*/
/*  V0.03  Release #3  Beta Version		1989.07.02  Y.Tagawa	*/
/*  V0.03a Fix few bug                          1989.07.03  Y.Tagawa    */
/*  V0.04  A lot of bugs fixed, strict mode     1990.01.13  Kai Uwe Rommel */
/*  V1.00  f and t commands, v option added     1990.01.27  Kai Uwe Rommel */
/*----------------------------------------------------------------------*/


#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifdef PROF
#include <profile.h>
#endif

#define STRICT
#define FASTCOPY

#ifdef MSDOS
#include <fcntl.h>
extern unsigned char _osmode;
extern FILE *popen();
extern pclose();
#define ftruncate chsize
#define mktemp Mktemp
#define SYSTIME_HAS_NO_TM
#define NOBSTRING
#define SYSNAME (_osmode ? "OS/2" : "MS-DOS")
#define OUR_EXTEND (_osmode ? EXTEND_OS2 : EXTEND_MSDOS)
#define FILENAME_LENGTH 128
#define NULLFILE "nul"
#define TMP_FILENAME_TEMPLATE "lhXXXXXX"
#define NOT_COMPATIBLE_MODE
#define RMODE "rb"
#define WMODE "wb"
#else
#include <sys/file.h>
#include <sys/time.h>
#define SYSNAME "UNIX"
#define OUR_EXTEND EXTEND_UNIX
#define FILENAME_LENGTH	1024
#define NULLFILE "/dev/null"
#define RMODE "r"
#define WMODE "w"
#endif

#ifdef SYSTIME_HAS_NO_TM
/* most of System V,  define SYSTIME_HAS_NO_TM */
#include <time.h>
#endif

/* #include <strings.h> */
#include <string.h>


/*----------------------------------------------------------------------*/
/*			DIRECTORY ACCESS STUFF				*/
/*----------------------------------------------------------------------*/
#ifndef NODIRECTORY
#ifdef SYSV_SYSTEM_DIR

#include <dirent.h>
#define DIRENTRY	struct dirent
#define NAMREN(p)	strlen (p->d_name)

#else	/* not SYSV_SYSTEM_DIR */

#ifdef NONSYSTEM_DIR_LIBRARY
#include "lhdir.h"
#else	/* not NONSYSTEM_DIR_LIBRARY */
#include <sys/dir.h>
#endif	/* not NONSYSTEM_DIR_LIBRARY */

#define DIRENTRY	struct direct
#define NAMLEN(p)	p->d_namlen

extern DIR *opendir ();
extern struct direct *readdir ();

#endif	/* not SYSV_SYSTEM_DIR */
#endif

/*----------------------------------------------------------------------*/
/*			FILE ATTRIBUTES					*/
/*----------------------------------------------------------------------*/

/* If file mode is not compatible between your Machine/OS and
   LHarc standard UNIX file mode.
   (See UNIX Manual stat(1), <sys/stat.h>,
   and/or below UNIX_* difinitions. ) */
/* #define NOT_COMPATIBLE_MODE */


/*----------------------------------------------------------------------*/
/*			MEMORY FUNCTIONS 				*/
/*----------------------------------------------------------------------*/

#ifdef NOBSTRING
#ifdef __ANSI__
#include "mem.h"
#define bcmp(a,b,n) memcmp ((a),(b),(n))
#define bcopy(s,d,n) memmove((d),(s),(n))
#define bzero(d,n) memset((d),0,(n))
#else	/* not __ANSI__ */
#include "memory.h"
#define bcmp(a,b,n) memcmp ((a),(b),(n))
#define bcopy(s,d,n) memcpy ((d),(s),(n))	/* movmem((s),(d),(n)) */
#define bzero(d,n) memset((d),0,(n))
#endif	/* not __ANSI__ */
#endif	/* NOBSTRING */


/*----------------------------------------------------------------------*/
/*			YOUR CUSTOMIZIES				*/
/*----------------------------------------------------------------------*/
/* These difinitions are changable to you like. */
/* #define ARCHIVENAME_EXTENTION	".LZH"		*/
/* #define TMP_FILENAME_TEMPLATE	"/tmp/lhXXXXXX"	*/
/* #define BACKUPNAME_EXTENTION		".BAK"		*/
/* #define MULTIBYTE_CHAR				*/



#define SJC_FIRST_P(c)			\
  (((unsigned char)(c) >= 0x80) &&	\
   (((unsigned char)(c) < 0xa0) ||	\
    ((unsigned char)(c) >= 0xe0) &&	\
    ((unsigned char)(c) < 0xfd)))
#define SJC_SECOND_P(c)			\
  (((unsigned char)(c) >= 0x40) &&	\
   ((unsigned char)(c) < 0xfd) &&	\
   ((ungigned char)(c) != 0x7f))

#ifdef MULTIBYTE_CHAR
#define MULTIBYTE_FIRST_P	SJC_FIRST_P
#define MULTIBYTE_SECOND_P	SJC_SECOND_P
#endif

/*----------------------------------------------------------------------*/
/*			OTHER DIFINITIONS				*/
/*----------------------------------------------------------------------*/

#ifndef SEEK_SET
#define SEEK_SET	0
#define SEEK_CUR	1
#define SEEK_END	2
#endif


/* non-integral functions */
extern struct tm *localtime ();
extern char *getenv ();
extern char *malloc ();
extern char *realloc ();

extern int rson[];

/* external variables */
extern int errno;


#define	FALSE	0
#define TRUE	1
typedef int boolean;


/*----------------------------------------------------------------------*/
/*		LHarc FILE DIFINITIONS					*/
/*----------------------------------------------------------------------*/
#define METHOD_TYPE_STRAGE	5
#define LZHUFF0_METHOD		"-lh0-"
#define LZHUFF1_METHOD		"-lh1-"
#define LARC4_METHOD		"-lz4-"
#define LARC5_METHOD		"-lz5-"

#define I_HEADER_SIZE			0
#define I_HEADER_CHECKSUM		1
#define I_METHOD			2
#define I_PACKED_SIZE			7
#define I_ORIGINAL_SIZE			11
#define I_LAST_MODIFIED_STAMP		15
#define I_ATTRIBUTE			19
#define I_NAME_LENGTH			21
#define I_NAME				22

#define I_CRC				22 /* + name_length */
#define I_EXTEND_TYPE			24 /* + name_length */
#define I_MINOR_VERSION			25 /* + name_length */
#define I_UNIX_LAST_MODIFIED_STAMP	26 /* + name_length */
#define I_UNIX_MODE			30 /* + name_length */
#define I_UNIX_UID			32 /* + name_length */
#define I_UNIX_GID			34 /* + name_length */
#define I_UNIX_EXTEND_BOTTOM		36 /* + name_length */



#define EXTEND_GENERIC  0
#define EXTEND_UNIX	'U'
#define EXTEND_MSDOS	'M'
#define EXTEND_MACOS	'm'
#define EXTEND_OS9	'9'
#define EXTEND_OS2	'2'
#define EXTEND_OS68K	'K'
#define EXTEND_OS386	'3'
#define EXTEND_HUMAN	'H'
#define EXTEND_CPM	'C'
#define EXTEND_FLEX	'F'

#define GENERIC_ATTRIBUTE		0x20
#define GENERIC_DIRECTORY_ATTRIBUTE	0x10

#define CURRENT_UNIX_MINOR_VERSION	0x00



typedef struct LzHeader {
  unsigned char		header_size;
  char			method[METHOD_TYPE_STRAGE];
  long			packed_size;
  long			original_size;
  long			last_modified_stamp;
  unsigned short	attribute;
  char			name[256];
  unsigned short	crc;
  boolean		has_crc;
  unsigned char		extend_type;
  unsigned char		minor_version;
  /*  extend_type == EXTEND_UNIX  and convert from other type. */
  time_t		unix_last_modified_stamp;
  unsigned short	unix_mode;
  unsigned short	unix_uid;
  unsigned short	unix_gid;
} LzHeader;

#define UNIX_FILE_TYPEMASK	0170000
#define UNIX_FILE_REGULAR	0100000
#define UNIX_FILE_DIRECTORY	0040000
#define UNIX_SETUID		0004000
#define UNIX_SETGID		0002000
#define UNIX_STYCKYBIT		0001000
#define UNIX_OWNER_READ_PERM	0000400
#define UNIX_OWNER_WRITE_PERM	0000200
#define UNIX_OWNER_EXEC_PERM	0000100
#define UNIX_GROUP_READ_PERM	0000040
#define UNIX_GROUP_WRITE_PERM	0000020
#define UNIX_GROUP_EXEC_PERM	0000010
#define UNIX_OTHER_READ_PERM	0000004
#define UNIX_OTHER_WRITE_PERM	0000002
#define UNIX_OTHER_EXEC_PERM	0000001
#define UNIX_RW_RW_RW		0000666

#define LZHEADER_STRAGE		256

/*----------------------------------------------------------------------*/
/*		PROGRAM 						*/
/*----------------------------------------------------------------------*/


#define CMD_UNKNOWN	0
#define CMD_EXTRACT	1
#define CMD_APPEND	2
#define CMD_VIEW	3

int      cmd = CMD_UNKNOWN;
char     **cmd_filev;
int      cmd_filec;
char     *archive_name;

char     expanded_archive_name[FILENAME_LENGTH];
char     temporary_name[FILENAME_LENGTH];
char     pager[FILENAME_LENGTH];


/* options */
boolean	quiet = FALSE;
boolean	text_mode = FALSE;
/*boolean  verbose = FALSE; */
boolean  noexec = FALSE; /* debugging option */
boolean  force = FALSE;
boolean  prof = FALSE;


/* view flags */
boolean  long_format_listing = FALSE;

/* extract flags */
boolean  output_to_test = FALSE;
boolean  output_to_stdout = FALSE;

/* append flags */
boolean  new_archive = FALSE;
boolean  update_if_newer = FALSE;
boolean  update_freshen = FALSE;
boolean  delete_after_append = FALSE;
boolean  delete_from_archive = FALSE;

boolean  remove_temporary_at_error = FALSE;


/*----------------------------------------------------------------------*/
/* NOTES :	Text File Format					*/
/*	GENERATOR		NewLine					*/
/*	[generic]		0D 0A					*/
/*	[MS-DOS]		0D 0A					*/
/*	[MacOS]			0D					*/
/*	[UNIX]			0A					*/
/*----------------------------------------------------------------------*/

char *myname;


void userbreak(void)
{
    error("Interrupt.");
}


main (argc, argv)
     int argc;
     char *argv[];
{
  char *p;

  myname = argv[0];
  signal(SIGINT, userbreak);

#ifdef PROF
  PROFINIT(PT_USER|PT_USEKP, NULL);
  PROFCLEAR(PT_USER);
  PROFON(PT_USER);
#endif

  if (argc < 3)
    print_tiny_usage_and_exit ();

  /* commands */
#ifdef MSDOS
  switch (tolower(argv[1][0]))
#else
  switch (argv[1][0])
#endif
    {
    case 'x':
    case 'e':
      cmd = CMD_EXTRACT;
      break;

    case 't':
      output_to_test = TRUE;
      cmd = CMD_EXTRACT;
      break;

    case 'p':
      output_to_stdout = TRUE;
      cmd = CMD_EXTRACT;
      break;

    case 'c':
      new_archive = TRUE;
      cmd = CMD_APPEND;
      break;

    case 'a':
      cmd = CMD_APPEND;
      break;

    case 'd':
      delete_from_archive = TRUE;
      cmd = CMD_APPEND;
      break;

    case 'u':
      update_if_newer = TRUE;
      cmd = CMD_APPEND;
      break;

    case 'f':
      update_if_newer = update_freshen = TRUE;
      cmd = CMD_APPEND;
      break;

    case 'm':
      delete_after_append = TRUE;
      cmd = CMD_APPEND;
      break;

    case 'v':
      cmd = CMD_VIEW;
      break;

    case 'l':
      long_format_listing = TRUE;
      cmd = CMD_VIEW;
      break;

    case 'h':
    default:
      print_tiny_usage_and_exit ();
    }

  /* options */
  p = &argv[1][1];
  for (p = &argv[1][1]; *p; p++)
    {
#ifdef MSDOS
      switch (tolower(*p))
#else
      switch (*p)
#endif
	{
	case 'q':	quiet = TRUE; break;
	case 'f':	force = TRUE; break;
/*      case 'p':       prof = TRUE; break; */
/*      case 'v':       verbose = TRUE; break; */
        case 'v':       strcpy(pager, p + 1); *(p + 1) = 0; break;
	case 't':	text_mode = TRUE; break;
	case 'n':	noexec = TRUE; break;

	default:
          fprintf (stderr, "unknown option '%c'.\n", *p);
	  exit (1);
        }
    }

  /* archive file name */
  archive_name = argv[2];

  /* target file name */
  cmd_filec = argc - 3;
  cmd_filev = argv + 3;
  sort_files ();

  switch (cmd)
    {
    case CMD_EXTRACT:	cmd_extract ();	break;
    case CMD_APPEND:	cmd_append ();	break;
    case CMD_VIEW:	cmd_view ();	break;
    }

#ifdef PROF
  PROFOFF(PT_USER);
  PROFDUMP(PT_USER, "profile.out");
  PROFFREE(PT_USER);
#endif

  exit (0);
}

print_tiny_usage_and_exit ()
{
  printf("\nC-LHarc for %s Version 1.00   (C) 1989-1990 Y.Tagawa, Kai Uwe Rommel\n"
         "\nUsage: %s {axevlufdmctp}[qnftv] archive_file [files or directories...]\n",
         SYSNAME, myname);
  printf("\nCommands:                    Options:\n"
         "  a   Append                   q   quiet\n"
         "  x,e EXtract                  n   no execute\n"
         "  v,l View/List                f   force (over write at extract)\n"
         "  u   Update                   t   files are TEXT files\n"
         "  f   Freshen                  v<pager>  use file pager for p command\n"
         "  d   Delete\n"
         "  m   Move\n"
         "  c   re-Construct new archive\n"
         "  t   Test archive\n"
         "  p   Print to STDOUT\n");
  exit (1);
}

message (title, msg)
     char *title, *msg;
{
  fprintf (stderr, "%s ", myname);
  if (errno == 0)
    fprintf (stderr, "%s %s\n", title, msg);
  else
    perror (msg);
}

warning (msg)
     char *msg;
{
  message ("Warning:", msg);
}

error (msg)
     char *msg;
{
  message ("Error:", msg);

  if (remove_temporary_at_error)
  {
#ifdef MSDOS
    fcloseall();
#endif
    unlink (temporary_name);
  }

  exit (1);
}

char *writting_filename;
char *reading_filename;

write_error ()
{
  error (writting_filename);
}

read_error ()
{
  error (reading_filename);
}



/*----------------------------------------------------------------------*/
/*									*/
/*----------------------------------------------------------------------*/

boolean expand_archive_name (dst, src)
     char *dst, *src;
{
  register char *p, *dot;

  strcpy (dst, src);

  for (p = dst, dot = (char*)0; *p; p++)
    if (*p == '.')
      dot = p;
    else if (*p == '/' || *p == '\\')
      dot = (char*)0;

  if (dot)
    p = dot;

#ifdef ARCHIVENAME_EXTENTION
  strcpy (p, ARCHIVENAME_EXTENTION);
#else
  strcpy (p, ".lzh");
#endif
  return (strcmp (dst, src) != 0);
}

#ifdef MSDOS
#define STRING_COMPARE(a,b) stricmp((a),(b))
#else
#define STRING_COMPARE(a,b) strcmp((a),(b))
#endif

int sort_by_ascii (a, b)
     char **a, **b;
{
  return STRING_COMPARE (*a, *b);
}

sort_files ()
{
  qsort (cmd_filev, cmd_filec, sizeof (char*), sort_by_ascii);
}

#ifndef MSDOS
char *strdup (string)
     char *string;
{
  int	len = strlen (string) + 1;
  char	*p = malloc (len);
  bcopy (string, p, len);
  return p;
}
#endif

#ifdef NODIRECTORY
/* please need your imprementation */
boolean find_files (name, v_filec, v_filev)
     char	*name;
     int	*v_filec;
     char	***v_filev;
{
  return FALSE;			/* DUMMY */
}
#else
boolean find_files (name, v_filec, v_filev)
     char	*name;
     int	*v_filec;
     char	***v_filev;
{
  char		newname[FILENAME_LENGTH];
  int 		len, n;
  DIR		*dirp;
  DIRENTRY	*dp;
  int           alloc_size = 64; /* any (^_^) */
  char		**filev;
  int		filec = 0;

  if ( strcmp(name, ".") == 0 )
    newname[0] = 0;
  else
    strcpy (newname, name);

  len = strlen (newname);
  dirp = opendir (name);

  if (dirp)
    {
      filev = (char**)malloc (alloc_size * sizeof(char *));
      if (!filev)
	error ("not enough memory");

      for (dp = readdir (dirp); dp != NULL; dp = readdir (dirp))
	{
	  n = NAMLEN (dp);
          if (
#ifndef MSDOS
              (dp->d_ino != 0) &&
#endif
              ((dp->d_name[0] != '.') ||
                 ((n != 1) &&
                 ((dp->d_name[1] != '.') ||
		 (n != 2)))) &&			/* exclude '.' and '..' */
	      (strcmp (dp->d_name, temporary_name) != 0) &&
	      (strcmp (dp->d_name, archive_name) != 0))
	    {
              if ((len != 0) && (newname[len-1] != '/') && (newname[len-1] != '\\'))
                {
#ifdef MSDOS
                  newname[len] = '\\';
#else
                  newname[len] = '/';
#endif
		  strncpy (newname+len+1, dp->d_name, n);
		  newname[len+n+1] = '\0';
		}
	      else
		{
		  strncpy (newname+len, dp->d_name, n);
		  newname[len+n] = '\0';
		}

	      filev[filec++] = strdup (newname);
	      if (filec == alloc_size)
		{
                  alloc_size += 64;
                  filev = (char**)realloc (filev, alloc_size * sizeof(char *));
		}
	    }
	}
      closedir (dirp);
    }

  *v_filev = filev;
  *v_filec = filec;
  if (dirp)
    {
      qsort (filev, filec, sizeof (char*), sort_by_ascii);
      return TRUE;
    }
  else
    return FALSE;
}
#endif

free_files (filec, filev)
     int	filec;
     char	**filev;
{
  int		i;

  for (i = 0; i < filec; i ++)
    free (filev[i]);

  free (filev);
}


/*----------------------------------------------------------------------*/
/*									*/
/*----------------------------------------------------------------------*/

int calc_sum (p, len)
     register char *p;
     register int len;
{
  register int sum;

  for (sum = 0; len; len--)
    sum += *p++;

  return sum & 0xff;
}

unsigned char *get_ptr;
#define setup_get(PTR) get_ptr = (unsigned char*)(PTR)
#define get_byte() (*get_ptr++)
#define put_ptr	get_ptr
#define setup_put(PTR) put_ptr = (unsigned char*)(PTR)
#define put_byte(c) *put_ptr++ = (unsigned char)(c)

unsigned short get_word ()
{
  int b0, b1;

  b0 = get_byte ();
  b1 = get_byte ();
  return (b1 << 8) + b0;
}

put_word (v)
     unsigned int	v;
{
  put_byte (v);
  put_byte (v >> 8);
}

long get_longword ()
{
  long b0, b1, b2, b3;

  b0 = get_byte ();
  b1 = get_byte ();
  b2 = get_byte ();
  b3 = get_byte ();
  return (b3 << 24) + (b2 << 16) + (b1 << 8) + b0;
}

put_longword (v)
     long v;
{
  put_byte (v);
  put_byte (v >> 8);
  put_byte (v >> 16);
  put_byte (v >> 24);
}


msdos_to_unix_filename (name, len)
     register char *name;
     register int len;
{
  register int i;

#ifdef MULTIBYTE_CHAR
  for (i = 0; i < len; i ++)
    {
      if (MULTIBYTE_FIRST_P (name[i]) &&
	  MULTIBYTE_SECOND_P (name[i+1]))
        i ++;
#ifndef MSDOS
      else if (name[i] == '\\')
        name[i] = '/';
#endif
      else if (isupper (name[i]))
	name[i] = tolower (name[i]);
    }
#else
  for (i = 0; i < len; i ++)
    {
#ifndef MSDOS
      if (name[i] == '\\')
	name[i] = '/';
      else
#endif
        if (isupper (name[i]))
          name[i] = tolower (name[i]);
    }
#endif
}

generic_to_unix_filename (name, len)
     register char *name;
     register int len;
{
  register int i;
  boolean	lower_case_used = FALSE;

#ifdef MULTIBYTE_CHAR
  for (i = 0; i < len; i ++)
    {
      if (MULTIBYTE_FIRST_P (name[i]) &&
	  MULTIBYTE_SECOND_P (name[i+1]))
	i ++;
      else if (islower (name[i]))
	{
	  lower_case_used = TRUE;
	  break;
	}
    }
  for (i = 0; i < len; i ++)
    {
      if (MULTIBYTE_FIRST_P (name[i]) &&
	  MULTIBYTE_SECOND_P (name[i+1]))
        i ++;
#ifndef MSDOS
      else if (name[i] == '\\')
        name[i] = '/';
#endif
      else if (!lower_case_used && isupper (name[i]))
	name[i] = tolower (name[i]);
    }
#else
  for (i = 0; i < len; i ++)
    if (islower (name[i]))
      {
	lower_case_used = TRUE;
	break;
      }
  for (i = 0; i < len; i ++)
    {
#ifndef MSDOS
      if (name[i] == '\\')
	name[i] = '/';
      else
#endif
        if (!lower_case_used && isupper (name[i]))
          name[i] = tolower (name[i]);
    }
#endif
}

macos_to_unix_filename (name, len)
     register char *name;
     register int len;
{
  register int i;

  for (i = 0; i < len; i ++)
    {
      if (name[i] == ':')
	name[i] = '/';
      else if (name[i] == '/')
	name[i] = ':';
    }
}

/*----------------------------------------------------------------------*/
/*									*/
/*	Generic stamp format:						*/
/*									*/
/*	 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16		*/
/*	|<-------- year ------->|<- month ->|<-- day -->|		*/
/*									*/
/*	 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0		*/
/*	|<--- hour --->|<---- minute --->|<- second*2 ->|		*/
/*									*/
/*----------------------------------------------------------------------*/


long gettz ()
{
#ifdef MSDOS
   return timezone;
#else
   struct timeval	tp;
   struct timezone	tzp;
   gettimeofday (&tp, &tzp);	/* specific to 4.3BSD */
/* return (tzp.tz_minuteswest * 60 + (tzp.tz_dsttime != 0 ? 60L * 60L : 0));*/
   return (tzp.tz_minuteswest * 60);
#endif
}

#ifdef NOT_USED
struct tm *msdos_to_unix_stamp_tm (a)
     long a;
{
  static struct tm t;
  t.tm_sec	= ( a          & 0x1f) * 2;
  t.tm_min	=  (a >>    5) & 0x3f;
  t.tm_hour	=  (a >>   11) & 0x1f;
  t.tm_mday	=  (a >>   16) & 0x1f;
  t.tm_mon	=  (a >> 16+5) & 0x0f - 1;
  t.tm_year	= ((a >> 16+9) & 0x7f) + 80;
  return &t;
}
#endif

time_t generic_to_unix_stamp (t)
     long t;
{
  struct tm tm;
  long longtime;
  static unsigned int dsboy[12] =
    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
  unsigned long days;

  tm.tm_year =  ((int)(t >> 25) & 0x7f) + 80;
  tm.tm_mon  =  ((int)(t >> 21) & 0x0f) - 1;     /* 0..11 means Jan..Dec */
  tm.tm_mday =  (int)(t >> 16) & 0x1f;         /* 1..31 means 1st,...31st */

  tm.tm_hour =  ((int)t >> 11) & 0x1f;
  tm.tm_min  =  ((int)t >> 5)  & 0x3f;
  tm.tm_sec  =  ((int)t        & 0x1f) * 2;

#ifdef MSDOS
  longtime = mktime(&tm);
#else
                                      /* Calculate days since 1970.01.01 */
  days = (365 * (tm.tm_year - 70) +   /* days due to whole years */
          (tm.tm_year - 70 + 1) / 4 + /* days due to leap years */
          dsboy[tm.tm_mon] +          /* days since beginning of this year */
          tm.tm_mday-1);              /* days since beginning of month */

  if ((tm.tm_year % 4 == 0) &&
      (tm.tm_year % 400 != 0) &&
      (tm.tm_mon >= 2))         /* if this is a leap year and month */
    days++;			/* is March or later, add a day */

  /* Knowing the days, we can find seconds */
  longtime = (((days * 24) + tm.tm_hour) * 60 + tm.tm_min) * 60 + tm.tm_sec;
  longtime += gettz ();      /* adjust for timezone */
#endif

  /* special case:  if MSDOS format date and time were zero, then we set
     time to be zero here too. */
  if (t == 0)
    longtime = 0;

  /* LONGTIME is now the time in seconds, since 1970/01/01 00:00:00.  */
  return (time_t)longtime;
}

long unix_to_generic_stamp (t)
     time_t t;
{
  struct tm *tm = localtime (&t);
  unsigned long stamp;

  stamp =  ( ((long)(tm->tm_year - 80)) << 25 );
  stamp += ( ((long)(tm->tm_mon + 1))   << 21 );
  stamp += ( ((long)(tm->tm_mday))      << 16 );
  stamp += ( ((long)(tm->tm_hour))      << 11 );
  stamp += ( ((long)(tm->tm_min))       << 5 );
  stamp += ( ((long)(tm->tm_sec))       >> 1 );

  return stamp;
}

/*----------------------------------------------------------------------*/
/*									*/
/*----------------------------------------------------------------------*/

boolean get_header (fp, hdr)
     FILE *fp;
     register LzHeader *hdr;
{
  int		header_size;
  int		name_length;
  char		data[LZHEADER_STRAGE];
  int		checksum;
  int		i;

  bzero (hdr, sizeof (LzHeader));

  if (((header_size = getc (fp)) == EOF) || (header_size == 0))
    {
      return FALSE;		/* finish */
    }

  if (fread (data + I_HEADER_CHECKSUM,
		  sizeof (char), header_size + 1, fp) < header_size + 1)
    {
      error ("Invalid header (LHarc file ?)\a");
      return FALSE;		/* finish */
    }

  setup_get (data + I_HEADER_CHECKSUM);
  checksum = calc_sum (data + I_METHOD, header_size);
  if (get_byte () != checksum)
    warning ("Checksum error (LHarc file?)\a");

  hdr->header_size = header_size;
  bcopy (data + I_METHOD, hdr->method, METHOD_TYPE_STRAGE);
#ifdef OLD
  if ((bcmp (hdr->method, LZHUFF1_METHOD, METHOD_TYPE_STRAGE) != 0) &&
      (bcmp (hdr->method, LZHUFF0_METHOD, METHOD_TYPE_STRAGE) != 0) &&
      (bcmp (hdr->method, LARC5_METHOD, METHOD_TYPE_STRAGE) != 0) &&
      (bcmp (hdr->method, LARC4_METHOD, METHOD_TYPE_STRAGE) != 0))
    {
      warning ("Unknown method (LHarc file ?)");
      return FALSE;		/* invalid method */
    }
#endif
  setup_get (data + I_PACKED_SIZE);
  hdr->packed_size	= get_longword ();
  hdr->original_size	= get_longword ();
  hdr->last_modified_stamp = get_longword ();
  hdr->attribute	= get_word ();
  name_length		= get_byte ();
  for (i = 0; i < name_length; i ++)
    hdr->name[i] =(char)get_byte ();
  hdr->name[name_length] = '\0';

  /* defaults for other type */
  hdr->unix_mode	= UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
  hdr->unix_gid 	= 0;
  hdr->unix_uid		= 0;

  if (header_size - name_length >= 24)
    {				/* EXTEND FORMAT */
      hdr->crc				= get_word ();
      hdr->extend_type			= get_byte ();
      hdr->minor_version		= get_byte ();
      hdr->has_crc = TRUE;
    }
  else if (header_size - name_length == 22)
    {				/* Generic with CRC */
      hdr->crc				= get_word ();
      hdr->extend_type			= EXTEND_GENERIC;
      hdr->has_crc = TRUE;
    }
  else if (header_size - name_length == 20)
    {				/* Generic no CRC */
      hdr->extend_type			= EXTEND_GENERIC;
      hdr->has_crc = FALSE;
    }
  else
    {
      warning ("Unknown header (LHarc file ?)");
      return FALSE;
    }

  switch (hdr->extend_type)
    {
    case EXTEND_MSDOS:
      msdos_to_unix_filename (hdr->name, name_length);
      hdr->unix_last_modified_stamp	=
	generic_to_unix_stamp (hdr->last_modified_stamp);
      break;

    case EXTEND_UNIX:
      hdr->unix_last_modified_stamp	= (time_t)get_longword ();
      hdr->unix_mode			= get_word ();
      hdr->unix_uid			= get_word ();
      hdr->unix_gid			= get_word ();
      break;

    case EXTEND_MACOS:
      macos_to_unix_filename (hdr->name, name_length);
      hdr->unix_last_modified_stamp	=
	generic_to_unix_stamp (hdr->last_modified_stamp);
      break;

    default:
      generic_to_unix_filename (hdr->name, name_length);
      hdr->unix_last_modified_stamp	=
	generic_to_unix_stamp (hdr->last_modified_stamp);
    }

  return TRUE;
}

init_header (name, v_stat, hdr)
     char *name;
     struct stat *v_stat;
     LzHeader *hdr;
{
  bcopy (LZHUFF1_METHOD, hdr->method, METHOD_TYPE_STRAGE);
  hdr->packed_size		= 0;
  hdr->original_size		= v_stat->st_size;
  hdr->last_modified_stamp      = unix_to_generic_stamp (v_stat->st_mtime);
#ifdef MSDOS
  getfilemode(name, &(hdr->attribute));
#else
  hdr->attribute                = GENERIC_ATTRIBUTE;
#endif
  strcpy (hdr->name, name);
  hdr->crc			= 0x0000;
  hdr->extend_type              = OUR_EXTEND;
  hdr->unix_last_modified_stamp	= v_stat->st_mtime;
				/* 00:00:00 since JAN.1.1970 */
#ifdef NOT_COMPATIBLE_MODE
  hdr->unix_mode		= v_stat->st_mode;
#else
  hdr->unix_mode		= v_stat->st_mode;
#endif

  hdr->unix_uid			= v_stat->st_uid;
  hdr->unix_gid			= v_stat->st_gid;

  if ((v_stat->st_mode & S_IFMT) == S_IFDIR)
    {
      bcopy (LZHUFF0_METHOD, hdr->method, METHOD_TYPE_STRAGE);
      hdr->attribute = GENERIC_DIRECTORY_ATTRIBUTE;
      hdr->original_size = 0;
      strcat (hdr->name, "/");
    }
}

/* Write only unix extended header. */
write_header (nafp, hdr)
     FILE *nafp;
     LzHeader *hdr;
{
  int		header_size;
  int		name_length;
  char          data[LZHEADER_STRAGE], *ptr;
  int           cnt;

  bzero (data, LZHEADER_STRAGE);
  bcopy (hdr->method, data + I_METHOD, METHOD_TYPE_STRAGE);
  setup_put (data + I_PACKED_SIZE);
  put_longword (hdr->packed_size);
  put_longword (hdr->original_size);
  put_longword (hdr->last_modified_stamp);
  put_word (hdr->attribute);
  
#ifdef STRICT

  if ( hdr->name[1] == ':' )
  {
    name_length = strlen(hdr->name + 2);
    put_byte (name_length);
    bcopy (hdr->name + 2, data + I_NAME, name_length);
  }
  else
  {
    name_length = strlen(hdr->name);
    put_byte (name_length);
    bcopy (hdr->name, data + I_NAME, name_length);
  }
  
  for ( ptr = data + I_NAME, cnt = 0; cnt < name_length; ptr++, cnt++ )
  {
    *ptr = toupper(*ptr);

    if ( *ptr == '/' )
      *ptr = '\\';
  }
#else
  name_length = strlen (hdr->name);
  put_byte (name_length);
  bcopy (hdr->name, data + I_NAME, name_length);
#endif
  
  setup_put (data + I_NAME + name_length);
  put_word (hdr->crc);
#ifdef STRICT
  header_size = I_EXTEND_TYPE - 2 + name_length;
#else
  put_byte (OUR_EXTEND);
  put_byte (CURRENT_UNIX_MINOR_VERSION);
  put_longword ((long)hdr->unix_last_modified_stamp);
  put_word (hdr->unix_mode);
  put_word (hdr->unix_uid);
  put_word (hdr->unix_gid);
  header_size = I_UNIX_EXTEND_BOTTOM - 2 + name_length;
#endif
  data[I_HEADER_SIZE] = header_size;
  data[I_HEADER_CHECKSUM] = calc_sum (data + I_METHOD, header_size);

  if (fwrite (data, sizeof (char), header_size + 2, nafp) == NULL)
    error ("cannot write to temporary file");
}

boolean archive_is_msdos_sfx1 (name)
     char *name;
{
  int	len = strlen (name);
  return ((len >= 4) &&
	  (strcmp (name + len - 4, ".com") == 0 ||
	   strcmp (name + len - 4, ".exe") == 0));
}

boolean skip_msdos_sfx1_code (fp)
     FILE *fp;
{
  unsigned char buffer[2048];
  unsigned char *p, *q;
  int	n;

  n = fread (buffer, sizeof (char), 2048, fp);

  for (p = buffer + 2, q = buffer + n - 5; p < q; p ++)
    {
      /* found "-l??-" keyword (as METHOD type string) */
      if (p[0] == '-' && p[1] == 'l' && p[4] == '-')
	{
	  /* size and checksum validate check */
	  if (p[-2] > 20 && p[-1] == calc_sum (p, p[-2]))
	    {
              fseek (fp, (long) ((p - 2) - buffer) - n, SEEK_CUR);
	      return TRUE;
	    }
	}
    }

  fseek (fp, (long) -n, SEEK_CUR);
  return FALSE;
}


/*----------------------------------------------------------------------*/
/*									*/
/*----------------------------------------------------------------------*/

make_tmp_name (original, name)
     char *original;
     char *name;
{
#ifdef TMP_FILENAME_TEMPLATE
  /* "/tmp/lhXXXXXX" etc. */
  strcpy (name, TMP_FILENAME_TEMPLATE);
#else
  char *p, *s;

  strcpy (name, original);
  for (p = name, s = (char*)0; *p; p++)
    if (*p == '/' || *p == '\\')
      s = p;

  strcpy ((s ? s+1 : name), "lhXXXXXX");
#endif

  mktemp (name);
}

make_backup_name (name, orginal)
     char *name;
     char *orginal;
{
  register char *p, *dot;

  strcpy (name, orginal);
  for (p = name, dot = (char*)0; *p; p ++)
    {
      if (*p == '.')
	dot = p;
      else if (*p == '/' || *p == '\\')
	dot = (char*)0;
    }

  if (dot)
    p = dot;

#ifdef BACKUPNAME_EXTENTION
  strcpy (p, BACKUPNAME_EXTENTION)
#else
  strcpy (p, ".bak");
#endif
}

make_standard_archive_name (name, orginal)
     char *name;
     char *orginal;
{
  register char *p, *dot;

  strcpy (name, orginal);
  for (p = name, dot = (char*)0; *p; p ++)
    {
      if (*p == '.')
	dot = p;
      else if (*p == '/' || *p == '\\')
	dot = (char*)0;
    }

  if (dot)
    p = dot;

#ifdef ARCHIVENAME_EXTENTION
  strcpy (p, ARCHIVENAME_EXTENTION);
#else
  strcpy (p, ".lzh");
#endif
}

/*----------------------------------------------------------------------*/
/*									*/
/*----------------------------------------------------------------------*/


boolean need_file (name)
     char *name;
{
  int	i;

  if (cmd_filec == 0)
    return TRUE;

  for (i = 0; i < cmd_filec; i ++)
    {
      if (STRING_COMPARE (cmd_filev[i], name) == 0)
	return TRUE;
    }

  return FALSE;
}

FILE *xfopen (name, mode)
     char *name, *mode;
{
  FILE *fp;

  if ((fp = fopen (name, mode)) == NULL)
    error (name);

  return fp;
}


/*----------------------------------------------------------------------*/
/*		Listing Stuff						*/
/*----------------------------------------------------------------------*/

/* need 14 or 22 (when long_format_listing is TRUE) column spaces */
print_size (packed_size, original_size)
     long packed_size, original_size;
{
  if (long_format_listing)
    printf ("%7ld ", packed_size);

  printf ("%7ld ", original_size);

  if (original_size == 0L)
    printf ("******");
  else
    printf ("%3d.%1d%%",
	    (int)((packed_size * 100L) / original_size),
	    (int)((packed_size * 1000L) / original_size) % 10);
}

/* need 12 or 17 (when long_format_listing is TRUE) column spaces */
print_stamp (t)
     time_t t;
{
  static boolean	got_now = FALSE;
  static time_t		now;
  static unsigned int	threshold;
  static char   t_month[12*3+1] = "JanFebMarAprMayJunJulAugSepOctNovDec";
  struct tm		*p;

  if (t == 0)
    {
      if (long_format_listing)
	printf ("                 "); /* 17 spaces */
      else
	printf ("            ");	/* 12 spaces */

      return;
    }

  if (!got_now)
    {
      time (&now);
      p = localtime (&now);
      threshold = p->tm_year * 12 + p->tm_mon - 6;
      got_now = TRUE;
    }

  p = localtime (&t);

  if (long_format_listing)
    printf ("%.3s %2d %02d:%02d %04d",
	    &t_month[p->tm_mon * 3], p->tm_mday,
	    p->tm_hour, p->tm_min, p->tm_year + 1900);
  else
    if (p->tm_year * 12 + p->tm_mon > threshold)
      printf ("%.3s %2d %02d:%02d",
	      &t_month[p->tm_mon * 3], p->tm_mday, p->tm_hour, p->tm_min);
    else
      printf ("%.3s %2d  %04d",
	      &t_month[p->tm_mon * 3], p->tm_mday, p->tm_year + 1900);
}

print_bar ()
{
  /* 17+1+(0 or 7+1)+7+1+6+1+(0 or 1+4)+(12 or 17)+1+20 */
  /*       12345678901234567_  1234567_123456  _123456789012   1234      */
  if (long_format_listing)
#ifdef STRICT
    printf ("------- ------- ------ ---- ----------------- -------------\n");
#else
    printf ("----------------- ------- ------- ------ ---- ----------------- -------------\n");
#endif
  else
#ifdef STRICT
    printf ("------- ------ ------------ --------------------\n");
#else
    printf ("----------------- ------- ------ ------------ --------------------\n");
#endif
}


/*
  view
 */
cmd_view ()
{
  FILE		*fp;
  LzHeader	hdr;
  register char	*p;
  long		a_packed_size = 0L;
  long		a_original_size = 0L;
  int		n_files = 0;
  struct stat	v_stat;

  if ((fp = fopen (archive_name, RMODE)) == NULL)
    if (!expand_archive_name (expanded_archive_name, archive_name))
      error (archive_name);
    else
      {
	errno = 0;
        fp = xfopen (expanded_archive_name, RMODE);
	archive_name = expanded_archive_name;
      }

  if (archive_is_msdos_sfx1 (archive_name))
    {
      skip_msdos_sfx1_code (fp);
    }

  if (!quiet)
    {
      /*       12345678901234567_  1234567_123456  _  123456789012  1234 */
#ifdef STRICT
      printf ("%s   SIZE  RATIO%s %s    STAMP   %s NAME\n",
#else
      printf (" PERMSSN  UID GID %s   SIZE  RATIO%s %s    STAMP   %s NAME\n",
#endif
	      long_format_listing ? " PACKED " : "", /* 8,0 */
	      long_format_listing ? "  CRC" : "", /* 5,0 */
	      long_format_listing ? "  " : "", /* 2,0 */
	      long_format_listing ? "   " : ""); /* 3,0 */
      print_bar ();
    }

  while (get_header (fp, &hdr))
    {
      if (need_file (hdr.name))
	{
	  if (hdr.extend_type == EXTEND_UNIX)
            {
#ifndef STRICT
              printf ("%c%c%c%c%c%c%c%c%c%4d/%-4d",
		  ((hdr.unix_mode & UNIX_OWNER_READ_PERM)  ? 'r' : '-'),
		  ((hdr.unix_mode & UNIX_OWNER_WRITE_PERM) ? 'w' : '-'),
		  ((hdr.unix_mode & UNIX_OWNER_EXEC_PERM)  ? 'x' : '-'),
		  ((hdr.unix_mode & UNIX_GROUP_READ_PERM)  ? 'r' : '-'),
		  ((hdr.unix_mode & UNIX_GROUP_WRITE_PERM) ? 'w' : '-'),
		  ((hdr.unix_mode & UNIX_GROUP_EXEC_PERM)  ? 'x' : '-'),
		  ((hdr.unix_mode & UNIX_OTHER_READ_PERM)  ? 'r' : '-'),
		  ((hdr.unix_mode & UNIX_OTHER_WRITE_PERM) ? 'w' : '-'),
		  ((hdr.unix_mode & UNIX_OTHER_EXEC_PERM)  ? 'x' : '-'),
		  hdr.unix_uid, hdr.unix_gid);
#endif
	    }
	  else
	    {
	      switch (hdr.extend_type)
		{			/* max 18 characters */
                case EXTEND_GENERIC:    p = "[Generic]"; break;

		case EXTEND_CPM:	p = "[CP/M]"; break;

		  /* OS-9 and FLEX's CPU is MC-6809. I like it. :-)  */
		case EXTEND_FLEX:	p = "[FLEX]"; break;

		case EXTEND_OS9:	p = "[OS-9]"; break;

		  /* I guessed from this ID.  Is this right? */
		case EXTEND_OS68K:	p = "[OS-9/68K]"; break;

		case EXTEND_MSDOS:	p = "[MS-DOS]"; break;

		  /* I have Macintosh. :-)  */
		case EXTEND_MACOS:	p = "[Mac OS]"; break;

		case EXTEND_OS2:	p = "[OS/2]"; break;

		case EXTEND_HUMAN:	p = "[Human68K]"; break;

		case EXTEND_OS386:	p = "[OS-386]"; break;

#ifdef EXTEND_TOWNSOS
		  /* This ID isn't fixed */
		case EXTEND_TOWNSOS:	p = "[TownsOS]"; break;
#endif

		  /* Ouch!  Please customize it's ID.  */
                default:                p = "[Unknown]"; break;
                }
#ifndef STRICT
              printf ("%-18.18s", p);
#endif
	    }

	  print_size (hdr.packed_size, hdr.original_size);

	  if (long_format_listing)
	    if (hdr.has_crc)
	      printf (" %04x", hdr.crc);
	    else
	      printf (" ****");

	  printf (" ");
	  print_stamp (hdr.unix_last_modified_stamp);
	  printf (" %s\n", hdr.name);
	  n_files ++;
	  a_packed_size += hdr.packed_size;
	  a_original_size += hdr.original_size;
	}
      fseek (fp, hdr.packed_size, SEEK_CUR);
    }

  fclose (fp);
  if (!quiet)
    {
      print_bar ();

#ifndef STRICT
      printf (" Total %4d file%c ",
	      n_files, (n_files == 1) ? ' ' : 's');
#endif
      print_size (a_packed_size, a_original_size);
      printf (" ");

      if (long_format_listing)
	printf ("     ");

      if (stat (archive_name, &v_stat) < 0)
	print_stamp ((time_t)0);
      else
	print_stamp (v_stat.st_mtime);

#ifdef STRICT
      printf (" %4d file%c ",
	      n_files, (n_files == 1) ? ' ' : 's');
#endif
      printf ("\n");
    }

  return;
}


boolean make_parent_path (name)
     char *name;
{
  char		path[FILENAME_LENGTH];
  struct stat	v_stat;
  register char	*p;

 /* make parent directory name into PATH for recursive call */
  strcpy (path, name);
  for (p = path + strlen (path); p > path; p --)
    if (p[-1] == '/' || p[-1] == '\\')
      {
	p[-1] = '\0';
	break;
      }

  if (p == path)
    return FALSE;		/* no more parent. */

  if (stat (path, &v_stat) >= 0)
    {
      if ((v_stat.st_mode & S_IFMT) != S_IFDIR)
	return FALSE;		/* already exist. but it isn't directory. */
      return TRUE;		/* already exist its directory. */
    }

  errno = 0;

  if (!quiet)
    message ("Making Directory", path);

  if (mkdir (path, 0777) >= 0)	/* try */
    return TRUE;		/* successful done. */

  errno = 0;

  if (!make_parent_path (path))
    return FALSE;

  if (mkdir (path, 0777) < 0)		/* try again */
    return FALSE;

  return TRUE;
}

FILE *open_with_make_path (name)
     char *name;
{
  FILE		*fp;
  struct stat	v_stat;
  char		buffer[1024];

  if (stat (name, &v_stat) >= 0)
    {
      if ((v_stat.st_mode & S_IFMT) != S_IFREG)
	return NULL;

      if (!force)
	{
	  for (;;)
	    {
	      fprintf (stderr, "%s OverWrite ?(Yes/No/All) ", name);
	      fflush (stderr);
	      gets (buffer);
	      if (buffer[0] == 'N' || buffer[0] == 'n')
		return NULL;
	      if (buffer[0] == 'Y' || buffer[0] == 'y')
		break;
	      if (buffer[0] == 'A' || buffer[0] == 'a')
		{
		  force = TRUE;
		  break;
		}
	    }
	}
    }

  fp = fopen (name, WMODE);
  if (!fp)
    {
      errno = 0;
      if (!make_parent_path (name))
	return NULL;

      fp = fopen (name, WMODE);
      if (!fp)
        message ("Error:", name);
    }
  return fp;
}

#ifdef MSDOS
void dosname(char *name)
{
  char *ptr, *first = NULL, *last = NULL;
  char temp[8];

  for ( ptr = strchr(name, 0); ptr >= name; ptr-- )
    if ( *ptr == '.' )
    {
      if ( last == NULL )
        last = ptr;
    }
    else if ( (*ptr == '/') || (*ptr == '\\') )
    {
      first = ptr + 1;
      break;
    }

  if ( first == NULL )
    first = name;
  if ( last == NULL )
    last = strchr(name, 0);

  for ( ptr = first; ptr < last; ptr++ )
    if ( *ptr == '.' )
      *ptr = '_';

  if ( strlen(last) > 4 )
    last[4] = 0;

  if ( last - first > 8 )
  {
    strcpy(temp, last);
    strcpy(first + 8, last);
  }
}
#endif

extern int decode_lzhuf (), decode_larc ();
extern int decode_stored_crc (), decode_stored_nocrc ();

extract_one (fp, hdr)
     FILE *fp;
     LzHeader *hdr;
{
  FILE		*ofp;		/* output file */
  char		name[1024];
  time_t	utimebuf[2];
  int		crc;
  int		(*decode_proc)(); /* (ifp,ofp,original_size,name) */
  int		save_quiet;

  strcpy (name, hdr->name);
  if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_REGULAR)
    {
      if (bcmp (hdr->method, LZHUFF1_METHOD, METHOD_TYPE_STRAGE) == 0)
	decode_proc = decode_lzhuf;
      else if ((bcmp (hdr->method, LZHUFF0_METHOD, METHOD_TYPE_STRAGE) == 0) ||
	       (bcmp (hdr->method, LARC4_METHOD, METHOD_TYPE_STRAGE) == 0))
	decode_proc = (hdr->has_crc) ? decode_stored_crc : decode_stored_nocrc;
      else if (bcmp (hdr->method, LARC5_METHOD, METHOD_TYPE_STRAGE) == 0)
	decode_proc = decode_larc;
      else
        message ("Error:", "Sorry, Cannot Extract this method.");

      reading_filename = archive_name;
      writting_filename = name;
      if (output_to_stdout)
	{
          if (!quiet)
            printf ("::::::::\r\n%s\r\n::::::::\r\n", name);

          if ( strlen(pager) != 0 )
            ofp = popen(pager, WMODE);
          else
            ofp = stdout;

	  save_quiet = quiet;
          quiet = TRUE;
          crc = (*decode_proc) (fp, ofp, hdr->original_size, name);
          quiet = save_quiet;

          if ( strlen(pager) != 0 )
            pclose(ofp);
	}
      else if (output_to_test)
        {
          ofp = fopen(NULLFILE, WMODE);
          crc = (*decode_proc) (fp, ofp, hdr->original_size, name);
          fclose(ofp);
        }
      else
        {
#ifdef MSDOS
          dosname(name);
#endif
	  if ((ofp = open_with_make_path (name)) == NULL)
	    return;
	  else
	    {
	      crc = (*decode_proc) (fp, ofp, hdr->original_size, name);
	      fclose (ofp);
	    }
	}

      if (hdr->has_crc && (crc != hdr->crc))
        if (output_to_test)
          message ("Error:", "CRC failed\a");
        else
          error ("CRC failed\a");
    }
  else
    {
      /* NAME has trailing SLASH '/', (^_^) */
      if (!output_to_stdout &&
	  !make_parent_path (name))
	error (name);
    }

  if (!output_to_stdout && !output_to_test)
    {
      utimebuf[0] = utimebuf[1] = hdr->unix_last_modified_stamp;
      utime (name, utimebuf);

#ifdef NOT_COMPATIBLE_MODE
      setfilemode(name, hdr->attribute);
#else
      chmod (name, hdr->unix_mode);
#endif

#ifndef MSDOS
      chown (name, hdr->unix_uid, hdr->unix_gid);
#endif
      errno = 0;
    }
}


/*
  extract
 */
cmd_extract ()
{
  LzHeader	hdr;
  long		pos;
  FILE		*fp;

  if ((fp = fopen (archive_name, RMODE)) == NULL)
    if (!expand_archive_name (expanded_archive_name, archive_name))
      error (archive_name);
    else
      {
	errno = 0;
        fp = xfopen (expanded_archive_name, RMODE);
	archive_name = expanded_archive_name;
      }

  if (archive_is_msdos_sfx1 (archive_name))
    {
      skip_msdos_sfx1_code (fp);
    }

  while (get_header (fp, &hdr))
    {
      if (need_file (hdr.name))
	{
	  pos = ftell (fp);
	  extract_one (fp, &hdr);
	  fseek (fp, pos + hdr.packed_size, SEEK_SET);
	} else {
	  fseek (fp, hdr.packed_size, SEEK_CUR);
	}
    }

  fclose (fp);

  return;
}

/*----------------------------------------------------------------------*/
/*									*/
/*----------------------------------------------------------------------*/

extern int encode_lzhuf ();
extern int encode_storerd_crc ();

append_one (fp, nafp, hdr)
     FILE *fp, *nafp;
     LzHeader *hdr;
{
  long	header_pos, next_pos, org_pos, data_pos;
  long	v_original_size, v_packed_size;

  reading_filename = hdr->name;
  writting_filename = temporary_name;

  org_pos = ftell (fp);
  header_pos = ftell (nafp);
  write_header (nafp, hdr);	/* DUMMY */

  if (hdr->original_size == 0)
    return;			/* previous write_header is not DUMMY. (^_^) */

  data_pos = ftell (nafp);
  hdr->crc = encode_lzhuf (fp, nafp, hdr->original_size,
			   &v_original_size, &v_packed_size, hdr->name);
  if (v_packed_size < v_original_size)
    {
      next_pos = ftell (nafp);
    }
  else
    {				/* retry by stored method */
      fseek (fp, org_pos, SEEK_SET);
      fseek (nafp, data_pos, SEEK_SET);
      hdr->crc = encode_stored_crc (fp, nafp, hdr->original_size,
				    &v_original_size, &v_packed_size);
      fflush (nafp);
      next_pos = ftell (nafp);
      ftruncate (fileno (nafp), next_pos);
      bcopy (LZHUFF0_METHOD, hdr->method, METHOD_TYPE_STRAGE);
    }
  hdr->original_size = v_original_size;
  hdr->packed_size = v_packed_size;
  fseek (nafp, header_pos, SEEK_SET);
  write_header (nafp, hdr);
  fseek (nafp, next_pos, SEEK_SET);
}

write_tail (nafp)
     FILE *nafp;
{
  putc (0x00, nafp);
}

copy_old_one (oafp, nafp, hdr)
     FILE *oafp, *nafp;
     LzHeader *hdr;
{
  if (noexec)
    {
      fseek (oafp, (long)(hdr->header_size + 2) + hdr->packed_size, SEEK_CUR);
    }
  else
    {
      reading_filename = archive_name;
      writting_filename = temporary_name;
      copy_file (oafp, nafp, (long)(hdr->header_size + 2) + hdr->packed_size);
    }
}


FILE *append_it (name, oafp, nafp)
     char *name;
     FILE *oafp, *nafp;
{
  LzHeader	ahdr, hdr;
  FILE		*fp;
  long		old_header;
  int		cmp;
  int		filec;
  char		**filev;
  int		i;

  struct stat	v_stat;
  boolean	directory;

  if (!delete_from_archive)
    if (stat (name, &v_stat) < 0)
      {
        message ("Error:", name);
        return oafp;
      }

  directory = ((v_stat.st_mode & S_IFMT) == S_IFDIR);

  init_header (name, &v_stat, &hdr);

  if (!delete_from_archive && !directory && !noexec)
    fp = xfopen (name, RMODE);

  while (oafp)
    {
      old_header = ftell (oafp);
      if (!get_header (oafp, &ahdr))
	{
	  fclose (oafp);
	  oafp = NULL;
	  break;
	}
      else
	{
	  cmp = STRING_COMPARE (ahdr.name, hdr.name);
	  if (cmp < 0)
	    {		/* SKIP */
	      fseek (oafp, old_header, SEEK_SET);
	      copy_old_one (oafp, nafp, &ahdr);
	    }
	  else if (cmp == 0)
	    {		/* REPLACE */
	      fseek (oafp, ahdr.packed_size, SEEK_CUR);
	      break;
	    }
	  else		/* cmp > 0, INSERT */
	    {
	      fseek (oafp, old_header, SEEK_SET);
	      break;
	    }
	}
    }

  if (delete_from_archive)
    {
      if (noexec)
        fprintf (stderr, "DELETE %s\n", name);
      else
        printf ("%s - Deleted\n", name);
    }
  else
    {
      if ( !oafp || (cmp > 0) || !update_if_newer
           || (ahdr.unix_last_modified_stamp < hdr.unix_last_modified_stamp) )
	{
	  if (noexec)
	    fprintf (stderr, "APPEND %s\n", name);
          else
#ifdef STRICT
            if ( !directory )
#endif
              if ( !update_freshen || (cmp == 0) )
                append_one (fp, nafp, &hdr);
	}
      else
	{					/* archive has old one */
	  fseek (oafp, old_header, SEEK_SET);
	  copy_old_one (oafp, nafp, &ahdr);
	}

      if (!directory)
	{
	  if (!noexec)
	    fclose (fp);
	}
      else
	{			/* recurcive call */
	  if (find_files (name, &filec, &filev))
	    {
	      for (i = 0; i < filec; i ++)
		oafp = append_it (filev[i], oafp, nafp);
	      free_files (filec, filev);
	    }
	  return oafp;
	}
    }

  return oafp;
}


remove_it (name)
     char *name;
{
  struct stat	v_stat;
  int		i;
  char		**filev;
  int		filec;

  if (stat (name, &v_stat) < 0)
    {
      fprintf (stderr, "Cannot access \"%s\".\n", name);
      return;
    }

  if ((v_stat.st_mode & S_IFMT) == S_IFDIR)
    {
      if (!find_files (name, &filec, &filev))
	{
          fprintf (stderr, "Cannot open directory \"%s\".\n", name);
	  return;
	}

      for (i = 0; i < filec; i ++)
	remove_it (filev[i]);

      free_files (filec, filev);

      if (noexec)
        printf ("REMOVE DIR %s\n", name);
      else if (rmdir (name) < 0)
        fprintf (stderr, "Cannot remove directory \"%s\".\n", name);
      else if (!quiet)
        printf ("%s - Removed\n", name);
    }
  else
    {
      if (noexec)
        printf ("REMOVE %s\n", name);
      else if (unlink (name) < 0)
        fprintf (stderr, "Cannot delete \"%s\".\n", name);
      else if (!quiet)
        printf ("%s - Removed\n", name);
    }
}

#ifdef FASTCOPY
#define BUFFER_SIZE 16384

#ifndef O_BINARY
#define O_BINARY 0
#endif

copy_archive(char *src, char *dst)
{
  int ih, oh;
  unsigned chunk;
  char *buffer = (char *) rson;

  printf ("Copying temp to archive ... ");

  ih = open (src, O_RDONLY | O_BINARY);
  if ( ih == -1 )
    error(src);
  oh = open (dst, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE);
  if ( oh == -1 )
    error(dst);

  while ( (chunk = read(ih, buffer, BUFFER_SIZE)) > 0 )
    if ( write(oh, buffer, chunk) != chunk )
      error(dst);

  close (ih);
  close (oh);

  printf("\b\b\b\b   \b\b\b\b.\n");
}
#endif

cmd_append ()
{
  LzHeader	ahdr;
  FILE		*oafp, *nafp;
  char		backup_archive_name [ FILENAME_LENGTH ];
  char		new_archive_name_buffer [ FILENAME_LENGTH ];
  char		*new_archive_name;
  int		i;
  long		old_header;
  struct stat	v_stat;
  boolean	old_archive_exist;

  if (cmd_filec == 0)
    return;

  make_tmp_name (archive_name, temporary_name);

  if ((oafp = fopen (archive_name, RMODE)) == NULL)
    if (expand_archive_name (expanded_archive_name, archive_name))
      {
	errno = 0;
        oafp = fopen (expanded_archive_name, RMODE);
	archive_name = expanded_archive_name;
      }

  old_archive_exist = (oafp) ? TRUE : FALSE;
  if (new_archive && oafp)
    {
      fclose (oafp);
      oafp = NULL;
    }

  if (oafp && archive_is_msdos_sfx1 (archive_name))
    {
      skip_msdos_sfx1_code (oafp);
      make_standard_archive_name (new_archive_name_buffer, archive_name);
      new_archive_name = new_archive_name_buffer;
    }
  else
    {
      new_archive_name = archive_name;
    }

  errno = 0;
  if (!noexec)
    {
      nafp = xfopen (temporary_name, WMODE);
      remove_temporary_at_error = TRUE;
    }

  for (i = 0; i < cmd_filec; i ++)
    oafp = append_it (cmd_filev[i], oafp, nafp);

  if (oafp)
    {
      old_header = ftell (oafp);
      while (get_header (oafp, &ahdr))
	{
	  fseek (oafp, old_header, SEEK_SET);
	  copy_old_one (oafp, nafp, &ahdr);
	  old_header = ftell (oafp);
	}
      fclose (oafp);
    }

  if (!noexec)
    {
      write_tail (nafp);
      fclose (nafp);
    }

  make_backup_name (backup_archive_name, archive_name);

  if (!noexec && old_archive_exist)
  {
    unlink(backup_archive_name);

    if (rename (archive_name, backup_archive_name) < 0)
      error (archive_name);
  }

  if (!quiet && new_archive_name == new_archive_name_buffer)
    {				/* warning at old archive is SFX */
      printf ("New Archive File is \"%s\"\n", new_archive_name);
    }

  if (!noexec && rename (temporary_name, new_archive_name) < 0)
    {
      if (stat (temporary_name, &v_stat) < 0)
	error (temporary_name);

#ifdef FASTCOPY
      copy_archive(temporary_name, archive_name);
#else
      oafp = xfopen (temporary_name, RMODE);
      nafp = xfopen (archive_name, WMODE);
      reading_filename = temporary_name;
      writting_filename = archive_name;
      copy_file (oafp, nafp, (long)v_stat.st_size);
      fclose (nafp);
      fclose (oafp);
#endif

      unlink (temporary_name);
    }
  remove_temporary_at_error = FALSE;

  if (delete_after_append)
    {
      if (!quiet && !noexec)
	printf ("Erasing...\n");
      for (i = 0; i < cmd_filec; i ++)
	remove_it (cmd_filev[i]);
    }

  return;
}
