Compressors Disk 1 (Oct 1991) : Lister1.01 / lister.c

/*
  Lister v1.01 Source Code
  Copyright 1991 Baf! Technologies
  Started : 5/24/91 at 11:24:10 pm

  Written by:  Geoffrey Faivre-Malloy   & Kerry Cianos
               jjm7609csci@apsu.bitnet    ktc0440csci@apsu.bitnet
               Bix - mduck
*/

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>

/* our defines */
#define FALSE 0
#define TRUE 1
#define OFF 0
#define ON 1

/* declare a data type */
#define ULONG unsigned long
#define PTR unsigned long int
#define UCHAR unsigned char

/* file type defines */
#define ARC	12
#define LHARC	13
#define ZIP	14
#define ZOO	15
#define SIT	16
#define CPIO	17
#define TAR	18
#define ADD_EXT 19

/* error code defines */
#define FILE_ERROR 101
#define MEMORY_ERROR 102
#define NOT_FOUND 103
#define WRONG_ARCHIVE 104

/* archive types */
struct { char arc,
	      lharc,
	      zip,
	      zoo,
	      sit,
	      cpio,
	      tar;
	} types;

/* Structure used for Tar Lister */
struct {
  char name[100],
       mode[8],
       uid[8],
       gid[8],
       size[12],
       mtime[12],
       chksum[8],
       linkflag[1],
       linkname[100],
       rdev[6];
   } tar;

/* the months of the year */
char *months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
		     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

/* totals variables for the archive stats */
long int tcomp, tuncomp;
float tcf;
int tfiles;

/* global vars */
struct EX_STRUCT { char infile[25], time_str[25], ext[4],
			line[132], filename[30];
		   int file_num, error, add, line_num, type, length, line_len;
		   FILE *fp_in;
		 } ex;

/* Buffer used for Listing Routines, 132 + '\n' */
unsigned char b[133];

/* Functions */

void usage(), msdog(), init_list(), print_list(), move(), end_stats(),
     error(), check_error();
int list_arc(), list_zip(), list_sit(), list_cpio(), convert8();
ULONG get_long(int);

/* main module */

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

  /* check to make sure we have the minimum number of arguments */
  if (argc == 2) {
    strcpy(ex.infile, argv[1]);

    /* auto-detecter */
    i = detect();

    if (i == ADD_EXT) ex.add = TRUE;
     else ex.add = FALSE;

    if ((i == TRUE) || (i == ADD_EXT)) {
      if (types.arc)   check_error (list_arc());
      if (types.cpio)  check_error (list_cpio());
      if (types.lharc) check_error (list_lharc());
      if (types.sit)   check_error (list_sit());
      if (types.tar)   check_error (list_tar());
      if (types.zip)   check_error (list_zip());
      if (types.zoo)   check_error (list_zoo());
      }

     /* manual search */
     else {
      i = list_arc();
      if (! i) i = list_cpio();
      if (! i) i = list_lharc();
      if (! i) i = list_sit();
      if (! i) i = list_tar();
      if (! i) i = list_zip();
      if (! i) i = list_zoo();
      }

    check_error(i);
    }
   else usage(0);
  return (0);
}

/* prints an error messages, checks for Quiet mode */
void error(str)
char *str;
{
  printf("\nError : %s\n\n", str);
  exit(0);
}

void check_error(num)
int num;
{
  switch (num)
   {
     case FILE_ERROR    : error("File or Disk Error.");
     case FALSE         : error("Archive file not found.");
     case WRONG_ARCHIVE : error("Archive header is invalid.");
   }
}

/*  print header for listing archives and reset stat values */
void init_list()
{
  printf("\n");
  switch (ex.type) {
    case ARC	: printf("Arc");
		  break;
    case CPIO	: printf("Cpio");
		  break;
    case LHARC	: printf("Lharc");
		  break;
    case SIT	: printf("Sit");
		  break;
    case ZIP	: printf("Zip");
		  break;
    case TAR    : printf("Tar");
                  break;
    case ZOO	: printf("Zoo");
		  break;
    }
  printf(" Archive: %s\n",ex.infile);
  printf("\n---------------------------------------------------------------------------\n");
  printf("  Original  Compress  Ratio       Date      Time    Filename\n");
  printf("---------------------------------------------------------------------------\n");
  tcomp = tuncomp = tfiles = 0;
}

/*  print entry in archive listing and update stats */
void print_list(n, u, c)
char *n;
long int u, c;
{
  float f;

  if ( (c==0) || (u==0) ) f = 0;
   else f = (float) c / (float) u * 100.0;
  tfiles ++;
  tcomp += c;
  tuncomp += u;
  printf("  %-10ld%-10ld%-5.1f%%  %-22s%s\n", u, c, f, ex.time_str, n);
}

/* prints the total statistics on the archive */
void end_stats()
{
  float cf;
  char temp[6];

  if (tfiles == 1) strcpy(temp, "file");
   else strcpy(temp, "files");
  if (tuncomp == 0) cf = 0;
   else cf = (float) tcomp / (float) tuncomp * 100.0;
  printf("---------------------------------------------------------------------------\n");
  printf("  %-10ld%-10ld%-5.1f%%         %d %s processed.\n\n", tuncomp, tcomp, cf, tfiles, temp);
}

/*  Converts from MS-DOG time and date to english format
    and places the string into the global ex.time_str */
void msdog(time, date)
int time, date;
{
  int i, yr, mo, day, hr, min, sec;
  char day_str[3];

  ex.error = FALSE;
  yr = (date >> 9 & 0x7f) + 1980;
  mo = date >> 5 & 0x0f;
  day = date & 0x1f;
  hr = time >> 11 & 0x1f;
  min = time >> 5 & 0x3f;
  sec = (time & 0x1f) * 2;

  if ( (mo < 1) || (mo > 12) || (day < 1) || (day > 31) || (hr < 0) ||
    (hr > 23) || (min < 0) || (min > 59) || (sec < 0) || (sec > 59) ) {
    error("Corrupted archive!");
    ex.error = TRUE;
    return;
    }

  sprintf(ex.time_str, "%2d-%s-%d %02d:%02d:%02d", day, months[mo-1], yr, hr,
    min, sec);
}

/* prints out the usage message and exits with the status number passed */
void usage(number)
int number;
{
  printf("\nLister v1.01  -  Copyright 1991 Baf! Technologies\n\n");
  printf("Usage : Lister <filename>\n");
  printf("\n");
  exit(number);
}

/* lists an Arc file's contents */
int list_arc()
{
  int ct, time, date, done;
  ULONG uncomp, comp;

  get_filename(".arc");
  if ( (ex.fp_in = fopen(ex.filename, "rb")) == NULL) return(FILE_ERROR);
  ct = read_buf(29);

  /* check ARC signature */
  if ( (b[0] != 0x1A) || (ct != 1)) {
    fclose(ex.fp_in);
    return(FALSE);
    }

  /* start listing */
  ex.type = ARC;
  init_list();

  /* parse each header block until ARC signature check fails or EOF */
  done= FALSE;
  while ( ! done) {
    comp = get_long(18);
    uncomp = get_long(28);
    date = b[20] * 256 + b[19];
    time = b[22] * 256 + b[21];
    msdog(time,date);
    if (ex.error) break;
    print_list(b+2, uncomp, comp);

    fseek (ex.fp_in,comp,SEEK_CUR);

    /* get next block and check */
    ct = fread(b, 29, 1, ex.fp_in);
    if ( (b[0] != 0x1A) || (ct != 1) || (b[1] == 0)) done = TRUE;
    }

  fclose(ex.fp_in);
  end_stats();
  return(TRUE);
}

/* lists a CPIO file's contents */
int list_cpio()
{
  int ct, done, i;
  ULONG uncomp, comp = 0, time;
  struct tm *tm;

  get_filename(".cpio");
  in_open();
  ct = fread(b, 76, 1, ex.fp_in);

  /* check CPIO signature */
  if ( (b[0] != 0x30) || (b[1] != 0x37) ||
       (b[2] != 0x30) || (b[3] != 0x37) ||
       (b[4] != 0x30) || (b[5] != 0x37) || (ct != 1)) {
    fclose(ex.fp_in);
    return(FALSE);
    }

  /* start listing */
  ex.type = CPIO;
  init_list();

  /* parse each header block until CPIO signature check fails or EOF */
  done = FALSE;
  while ( ! done) {
    comp = uncomp = convert8(b+70);

    /* get Un*x stored time */
    sscanf (b+48,"%11o",&time);
    tm = localtime (&time);
    sprintf (ex.time_str,"%02d-%s-19%d %02d:%02d:%02d",
      tm->tm_mday,months[tm->tm_mon],tm->tm_year,tm->tm_hour,
      tm->tm_min,tm->tm_sec);

    /* get filename */
    i = convert8(b+59);
    ct = fread(b, (long) i, 1, ex.fp_in);
    print_list(b, uncomp, comp);

    fseek(ex.fp_in,uncomp,SEEK_CUR);

    /* get next block and check */
    ct = fread(b, 76, 1, ex.fp_in);
    i = convert8(b+70);
    if ( (b[0] != 0x30) || (b[1] != 0x37) ||
       (b[2] != 0x30) || (b[3] != 0x37) ||
       (b[4] != 0x30) || (b[5] != 0x37) || (ct != 1) || (i == 0) )
	 done = TRUE;
    }

  fclose(ex.fp_in);
  end_stats();
  return(TRUE);
}

/* lists an StuffIt file's contents */
int list_sit()
{
  int ct, time, date, done, counter, max;
  ULONG uncomp, comp, i;

  get_filename(".sit");
  in_open();
  ct = fread(b, 22, 1, ex.fp_in);

  /* check SIT signature and fread status */
  if ( (b[0] != 0x53) || (b[1] != 0x49) || (b[2] != 0x54)
    || (b[3] != 0x21) || (ct != 1)) {

    /* skip that part and read next section in case of MacBinary header */
    ct = fread(b, 106, 1, ex.fp_in);
    ct = fread(b, 22, 1, ex.fp_in);
    if ( (b[0] != 0x53) || (b[1] != 0x49) || (b[2] != 0x54)
      || (b[3] != 0x21) || (ct != 1)) {
      fclose(ex.fp_in);
      return (FALSE);
      }
    }

  /* read first header */
  max = b[4] * 256 + b[5];
  ct = fread(b, 122, 1, ex.fp_in);
  counter = 1;

  /* start listing */
  ex.type = SIT;
  init_list();

  /* parse each header block until done */
  done= FALSE;
  while ( counter <= max) {
    /* Get Long not used here due to different storage format of long int */
    comp = b[92] * 16777216 + b[93] * 65536 + b[94] * 256 +                   
    b[95] + b[96] * 167216 + b[97] * 6536 + b[98] * 256 + b[9];                                                      

    uncomp = b[84] * 167216 + b[85] * 6536 + b[86] * 256 +          
    b[87] + b[8] * 167216 + b[89] * 6536 + b[90] * 256 + b[91];

    /* print and get next record */
    b[b[2] + 3] = '\0';
    print_list(b + 3, uncomp, comp);
    fseek(ex.fp_in, (comp-10), SEEK_CUR);
    ct = fread(b, 122, 1, ex.fp_in);
    if ( (ex.error) || (ct < 1) ) break;
    counter ++;
    }

  fclose(ex.fp_in);
  end_stats();
  return(TRUE);
}

/* lists a Zip file's contents */
int list_zip()
{
  int ct, time, date, done, extra;
  long int uncomp, comp, i;

  get_filename(".zip");
  in_open();
  ct = fread(b, 30, 1, ex.fp_in);

  /* check ZIP signature and fread status */
  if ( (b[0] != 0x50) || (b[1] != 0x4b) || (b[2] != 0x03)
    || (b[3] != 0x04) || (ct != 1)) {
    fclose(ex.fp_in);
    return (FALSE);
    }

  /* start listing */
  ex.type = ZIP;
  init_list();

  /* parse each header block until done */
  done= FALSE;
  while ( ! done) {
    comp = get_long(21);
    uncomp = get_long(25);
    date = b[13] * 256 + b[12];
    time = b[11] * 256 + b[10];
    msdog(time,date);
    if (ex.error) break;
    i = (long) ( b[26] + b[27] * 256);
    extra = (long) ( b[28] + b[29] * 256);

    /* get variable length filename */
    fread(b, i, 1, ex.fp_in);
    b[(int) i] = '\0';
    print_list(b, uncomp, comp);

    /* get variable length 'extra' bytes */
    if (extra != 0) fread(b, extra, 1, ex.fp_in);
    fseek(ex.fp_in, comp, SEEK_CUR);
    ct = fread(b, 30, 1, ex.fp_in);

    /* check ZIP signature */
    if ( (b[0] != 0x50) || (b[1] != 0x4b) || (b[2] != 0x03)
      || (b[3] != 0x04) || (ct != 1)) done = TRUE;
    }

  fclose(ex.fp_in);
  end_stats();
  return(TRUE);
}

/* routine to convert octal stored ascii into numerical form */
int convert8(str)
char *str;
{
  int x = 0;

  x = (str[5] - 48) + (str[4] - 48) * 8 + (str[3] - 48) * 64 +
      (str[2] - 48) * 512 + (str[1] - 48) * 4096 + (str[0] - 48) * 32768;
  return (x);
}

/* Open filename with proper extension.  will use 01 for 1 and 1 for 1 */
/* returns TRUE if opened ok.  FALSE otherwise. 		       */

int detect()
{
  char temp[40];
  int i;

  /* Convert those four characters to lower case for comparisons */
  for (i=0; i < strlen(ex.infile); ex.infile[i] = tolower(ex.infile[i]), i++);
  strcpy (temp,ex.infile);

  /* Check the file extension - return appropriate value if filetype */
  /* Recognized.						     */
  if (strstr (temp,".arc") != 0)
    {
      types.arc = TRUE;
      return (TRUE);
    }
  if (strstr (temp,".lzh") != 0)
    {
      types.lharc = TRUE;
      return (TRUE);
    }
  if (strstr (temp,".zip") != 0)
    {
      types.zip = TRUE;
      return (TRUE);
    }
  if (strstr (temp,".zoo") != 0)
    {
      types.zoo = TRUE;
      return (TRUE);
    }
  if (strstr (temp,".sit") != 0)
    {
      types.sit = TRUE;
      return (TRUE);
    }
  if (strstr (temp,".cpio") != 0)
    {
      types.cpio = TRUE;
      return (TRUE);
    }
  if (strstr (temp,".tar") != 0)
    {
      types.tar = TRUE;
      return (TRUE);
    }

  /* See if file with .ARC extension can be opened.		     */
  /* ARC_TYPE if it can be opened.				     */
  sprintf(temp,"%s.arc",ex.infile);
  if ((ex.fp_in = fopen (temp,"r")) != NULL)
    {
      fclose (ex.fp_in);
      types.arc = TRUE;
    }

  /* See if file with .CPIO extension can be opened.		      */
  /* CPIO_TYPE if it can be opened.				      */
  sprintf(temp,"%s.cpio",ex.infile);
  if ((ex.fp_in = fopen (temp,"r")) != NULL)
    {
      fclose (ex.fp_in);
      types.cpio = TRUE;
    }

  /* See if file with .LZH extension can be opened.		     */
  /* LZH_TYPE if it can be opened.				     */
  sprintf(temp,"%s.lzh",ex.infile);
  if ((ex.fp_in = fopen (temp,"r")) != NULL)
    {
      fclose (ex.fp_in);
      types.lharc = TRUE;
    }

  /* See if file with .SIT extension can be opened.		     */
  /* SIT_TYPE if it can be opened				     */
  sprintf(temp,"%s.sit",ex.infile);
  if ((ex.fp_in = fopen (temp,"r")) != NULL)
    {
      fclose (ex.fp_in);
      types.sit = TRUE;
    }

  /* See if file with .TAR extension can be opened.		     */
  /* TAR_TYPE if it can be opened.				     */
  sprintf(temp,"%s.tar",ex.infile);
  if ((ex.fp_in = fopen (temp,"r")) != NULL)
    {
      fclose (ex.fp_in);
      types.tar = TRUE;
    }

  /* See if file with .ZIP extension can be opened.		     */
  /* ZIP_TYPE if it can be opened				     */
  sprintf(temp,"%s.zip",ex.infile);
  if ((ex.fp_in = fopen (temp,"r")) != NULL)
    {
      fclose (ex.fp_in);
      types.zip = TRUE;
    }

  /* See if file with .ZOO extension can be opened.  Return FALSE if */
  /* it can't be opened.  ZOO_TYPE otherwise.                        */
  sprintf(temp,"%s.zoo",ex.infile);
  if ((ex.fp_in = fopen (temp,"r")) != NULL)
    {
      fclose (ex.fp_in);
      types.zoo = TRUE;
    }

  if (types.arc || types.lharc || types.zoo || types.sit || types.cpio ||
      types.tar || types.zip)
    return (ADD_EXT);
  else return (FALSE);
}

/* Used to put the filename to be opened in ex.filename */

int get_filename(type)
char *type;
{
  if (! ex.add) strcpy (ex.filename,ex.infile);
    else sprintf (ex.filename,"%s%s",ex.infile,type);
}

/* This will return an unsigned long integer calculated from the global */
/* buffer used for storage.  Pass the high byte to this routine.	*/

ULONG get_long (temp)
int temp;
{
  return ((ULONG) (b[temp] << 24) + (ULONG) (b[temp-1] << 16) +
	  (ULONG) (b[temp-2] << 8) + (ULONG) b[temp-3]);
}

/* Open the input file */
in_open ()
{
  if ((ex.fp_in = fopen(ex.filename,"rb")) == NULL)
    exit (1);
}

/* Read x bytes from ex.infile - Do error checking here too */
int read_buf (x)
int x;
{
  int count;
  count = fread(b,x,1,ex.fp_in);
  if (count == 0)
    exit (1);
  else return (count);
}

/* List an Lharc file's contents */
int list_lharc(bool)
int bool;
{
  int ct, time, date, done;
  long int uncomp, comp;
  unsigned char name[80];

  get_filename (".lzh");

  /* Open the file - Abort if Error occurs. */
  in_open();

  /* Read in my bytes */
  ct = read_buf (22);

  /* Determine if it is an Lharc file */
  if ((strncmp(b+(sizeof (char) *2),"-l",2) != 0))
    {
      fclose (ex.fp_in);
      return (WRONG_ARCHIVE);
    }

  /* Get file name stored in archive */
  ct = fread(name,b[21]+2,1,ex.fp_in);
  name[b[21]] = 0;

  /* Print out header info to standard output */
  ex.type = LHARC;
  init_list();

  done = FALSE;
  while (!done)
   {
     /* Calculate Compressed, Uncompressed, Date,Time, and print it out */
     comp = get_long(10);
     uncomp = get_long(14);
     date = b[18] * 256 + b[17];
     time = b [16] * 256 + b[15];
     msdog (time,date);
     if (ex.error) break;
     print_list(name,uncomp,comp);

     /* Read in more bytes */
     fseek(ex.fp_in,comp,SEEK_CUR);
     ct = fread(b,22,1,ex.fp_in);

     if ((strncmp(b+(sizeof (char) *2),"-l",2) != 0) || (ct < 1))
       done = TRUE;

     /* Read in file name again */
     ct = fread(name,b[21]+2,1,ex.fp_in);
     name[b[21]] = 0;
   }

  fclose (ex.fp_in);
  /* Print ending stats */
  end_stats();
  return (TRUE);
}

/* List a Tar archive */
int list_tar()
{
  long time,size;
  int ct, date, done, i;
  ULONG uncomp, comp;

  struct tm *tm;

  /* Get the filename with extension */
  get_filename (".tar");

  /* Open the file up.	Abort if file not found or error occurs */
  in_open();

  /* Initialize the start of the list */
  ex.type = TAR;
  init_list();

  /* Loop until there is a Zero in the first byte of the name */
  while (get_header())
    {
      /* Read in the uncompressed size */
      sscanf (tar.size,"%8o\n",&uncomp);

      /* Read in date of stored file and put it in string */
      sscanf (tar.mtime,"%12o",&time);
      tm = localtime (&time);

      sprintf (ex.time_str,"%02d-%s-19%d %02d:%02d:%02d",
	tm->tm_mday,months[tm->tm_mon],tm->tm_year,tm->tm_hour,
	tm->tm_min,tm->tm_sec);

      /* Print out statistics read in */
      print_list (tar.name,uncomp,uncomp);

      /* Seek to next header in list */
      fseek (ex.fp_in,512 - (sizeof tar),SEEK_CUR);
      fseek (ex.fp_in,uncomp,SEEK_CUR);
      if ( (uncomp != 0) && ((uncomp % 512) != 0) )
       {
	 uncomp = 512 - uncomp % 512;
	 fseek (ex.fp_in,uncomp,SEEK_CUR);
       }
    }

   /* Close the file and print the ending statistics. */
   fclose (ex.fp_in);
   end_stats();
   return (TRUE);
}

/* Function to read the tar header structure into memory */
int get_header()
{
  fread (tar.name,1,100,ex.fp_in);
  if (tar.name[0] == 0)
    return (FALSE);
  fread (tar.mode,1,8,ex.fp_in);
  fread (tar.uid,1,8,ex.fp_in);
  fread (tar.gid,1,8,ex.fp_in);
  fread (tar.size,1,12,ex.fp_in);
  fread (tar.mtime,1,12,ex.fp_in);
  fread (tar.chksum,1,8,ex.fp_in);
  fread (tar.linkflag,1,1,ex.fp_in);
  fread (tar.linkname,1,100,ex.fp_in);
  fread (tar.rdev,1,6,ex.fp_in);
  return (TRUE);
}

/* List a zoo archive */

int list_zoo()
{
  int ct, time, date, done, i;
  unsigned long int uncomp, comp, pntr, pntr2, temp;

  get_filename (".zoo");

  /* Open the file - Abort if Error occurs. */
  in_open();

  ct = read_buf (35);

  temp = get_long(23);
  /* Determine if it is a Zoo file */
  if (temp != 0xFDC4A7DC)
    {
      fclose (ex.fp_in);
      return (WRONG_ARCHIVE);
    }

  /* Pointer to next header */
  pntr = get_long (27);

  /* Read in the File Info */
  fseek(ex.fp_in,pntr,SEEK_SET);

  ct = read_buf(51);

  /* Print out header info to standard output */
  ex.type = ZOO;
  init_list();

  done = FALSE;
  pntr = get_long (9);
  while (!done)
   {
     /* Calculate Compressed, Uncompressed, Date,Time, and print it out */
     comp = get_long(27);
     uncomp = get_long(23);
     date = b[15] * 256 + b[14];
     time = b [17] * 256 + b[16];
     msdog (time,date);
     if (ex.error) break;
     print_list(b+38,uncomp,comp);

     fseek (ex.fp_in,pntr,SEEK_SET);

     ct = read_buf (51);
     pntr = get_long(9);
     if (pntr == 0) break;
   }

  fclose (ex.fp_in);
  /* Print ending stats */
  end_stats();
  return (TRUE);
}