/* zoolist.c */
/*
Copyright (C) 1986 Rahul Dhesi -- All rights reserved
*/
#include "options.h"
#include "portable.h"
#include "zoomem.h"  /* to get ZOOCOUNT */

/* Lists files in archive */
#include "zoo.h"
#include "errors.i"
#include <stdio.h>
#include "various.h"
#include "zoofns.h"

static char tot_fmt[] = "%8lu %3u%% %8lu  %4d file%s\n"; 
/* static char tot_fmt[] = "%8lu %3u%%  %8lu   %23d files\n"; */
/* static char tot_fmt[] = "TOTAL %4d    %8lu %3u%%  %8lu\n"; */
static char tot_line[] = 
   /* "------------  --------  ---  --------  --------- --------\n"; */
   "--------  --- --------  --------- --------\n";

static char dbl_percent[] = "Archive %s:  %s";

#ifdef LINT_ARGS
void show_comment (struct direntry *, FILE *, int, char *);
int ver_too_high (struct zoo_header *);
#endif

void zoolist (argv, option, argc)
char **argv, *option;
int argc;
{
char whichname[PATHSIZE];  /* which name to use */
char *this_zoo;            /* currently matched archive name */
register FILE *zoo_file;
char *flist[ZOOCOUNT];       /* list of ptrs to input archive names */
int fptr;                  /* will point to within list of archive names */

struct direntry direntry;
struct zoo_header zoo_header;
int size_factor;
char packing[9];
unsigned long tot_org_siz = 0L, tot_siz_now = 0L;
int   tot_sf;
int file_count = 0;
int del_count = 0;                  /* number of deleted entries */
int bad_pack;                 /* 1 if packing method is unknown */
static char *month_list="000JanFebMarAprMayJunJulAugSepOctNovDec";
static char dashes[] = "------------\n";
int year, month, day, hours, min, sec;
int list_deleted = 0;         /* list deleted files too */
int fast = 0;                 /* fast list */
int column;                   /* for column printing */
long fiz_ofs = 0;             /* offset where to start */
int verb_list = 0;            /* if verbose listing needed */
int show_name = 0;            /* if archive name to be included in listing */
int zoocount = 1;             /* number of archives to list */
int biglist = 0;              /* multiarchive listing */
int longest;                  /* length of longest archive name */

while (*option) {
   switch (*option) {
      case 'a': show_name++; break;
      case 'd': list_deleted++; break;
      case 'f': fast++; break;
      case 'c':
      case 'v': verb_list++; break;
      case 'l': break;
      case 'L': biglist++; zoocount = argc; break;
      case '@': 
         ++option;
         fiz_ofs = calc_ofs(option); 
         goto no_more;
      default:
         prterror ('w', option_ignored, *option);
   }
   option++;
}

no_more:  /* come from exit from while loop above */

if (fast && show_name) {      /* don't allow 'a' with 'f' */
   show_name = 0;
   prterror ('w', option_ignored, 'a');
}

#ifdef WILDCARDS
   /* For each archive name supplied, if it is not a char range and
      does not contain a dot, append "*.zoo". */
   {
      int i;
      for (i = 0; i < argc;  i++) {
         if (strchr (nameptr (argv[i]), EXT_CH) == NULL && 
                           !match_half (nameptr (argv[0]), "?-?"))
            argv[i] = newcat (argv[i], "*.zoo");
      }
   }
#endif

makelist (zoocount, argv, flist,        ZOOCOUNT-2,   ".",".","..", &longest);
/*        ^argc     ^argv ^list_pointer ^max_no_files   ^exclude */

for (fptr = 0;  (this_zoo = flist[fptr]) != NULL; fptr++) {
   int ercount;                  /* count of errors */
   int entrycount;               /* count of directory entries */
   int column;                   /* for column printing */
   int expl_deleted;             /* explain what D means */
   int expl_comment;             /* explain what comment means */
   int expl_ver;                 /* Explain what V means */
   int expl_star;                /* Explain what * means */
   int first_time;               /* first time through loop for an archive */

   ercount = entrycount = column = del_count =
      expl_deleted = expl_comment = expl_ver = expl_star = 0;
   first_time = 1;

#ifndef WILDCARDS
   /* Add default extension if none supplied */
   if (strchr (nameptr (this_zoo), EXT_CH) == NULL)
      this_zoo = newcat (this_zoo, EXT_DFLT);
#endif

   zoo_file = fopen (this_zoo, FRDSTR);

   if (zoo_file == NULL) {
      prterror ('e', could_not_open, this_zoo);
      continue;
   } else if (!show_name)
      printf ("\nArchive %s:\n", this_zoo);
   
if (fiz_ofs != 0L) {                /* if offset specified, start there */
   prterror('m', "Starting at %ld\n", fiz_ofs);
   fseek (zoo_file, fiz_ofs, 0);
} else {
   if (frd_zooh (&zoo_header, zoo_file) == -1 ||
                                             zoo_header.zoo_tag != ZOO_TAG) {
      prterror ('e', dbl_percent, this_zoo, invalid_header);
      goto loop_end;
   }
   /* Seek to the beginning of the first directory entry */
   if (fseek (zoo_file, zoo_header.zoo_start, 0) != 0) {
      ercount++;
      prterror ('e', dbl_percent, this_zoo, bad_directory);
      goto loop_end;
   }
   if (!show_name && ver_too_high (&zoo_header)) {
      ercount++;
      if (ercount < 2) {
         prterror ('w', dbl_percent, this_zoo, "");
         prterror ('M', wrong_version, zoo_header.major_ver, zoo_header.minor_ver);
      }
   }
} /* end if (fiz_ofs !- 0L) */

   /* Now we print information about each file in the archive */
   
   if (!show_name) { /* initialize for each file only if not disk catalog */
      tot_org_siz = 0L;  
      tot_siz_now = 0L;
      file_count = 0;
      del_count = 0;
   }

   while (1) {
      if (readdir (&direntry, zoo_file, 0) == -1) {
         prterror ('F', dbl_percent, this_zoo, bad_directory);
         goto givesummary;
      }
      if (direntry.zoo_tag != ZOO_TAG) {
         long currpos, zoolength;
         prterror ('F', dbl_percent, this_zoo, invalid_header);
         if ((currpos = ftell (zoo_file)) != -1L)
            if (fseek (zoo_file, 0L, 2) == 0)
               if ((zoolength = ftell (zoo_file)) != -1L)
                  printf (cant_process, zoolength - currpos);              
         goto givesummary;
      }
   
      if (direntry.next == 0L)      /* EXIT on end of chain */
         break;                                 
      else
         entrycount++;              /* Number of directory entries */
   
      if (direntry.deleted)
         ++del_count;
      
      combine (whichname,
               direntry.dirlen > 0 ? direntry.dirname : "", 
               (direntry.namlen > 0) ? direntry.lfname : direntry.fname
              );
#ifdef DEBUG
      printf("matching against [%s] and [%s]\n", 
               nameptr(whichname), whichname);
#endif

      if ( ( (list_deleted && direntry.deleted) ||
               (list_deleted < 2 && !direntry.deleted)
           ) 
              && (biglist || needed(whichname))) {
   
         file_count++;
   
         if (direntry.packing_method > MAX_PACK) {
            bad_pack = 1;
            expl_ver = 1;
         }  else
            bad_pack = 0;
      
         size_factor = cfactor (direntry.org_size, direntry.size_now);
   
         year  =  ((unsigned int) direntry.date >> 9) & 0x7f;
         month =  ((unsigned int) direntry.date >> 5) & 0x0f;
         day   =  direntry.date        & 0x1f;
   
         hours =  ((unsigned int) direntry.time >> 11)& 0x1f;
         min   =  ((unsigned int) direntry.time >> 5) & 0x3f;
         sec   =  ((unsigned int) direntry.time & 0x1f) * 2;
   
         if (fast) {
            if ((column++ % 5) == 0 && !first_time)
               fputchar ('\n');
            printf("%-12s  ", direntry.fname);
   
         } else {
            if (first_time && !show_name) {     /* print archive header */
               printf ("Length    CF  Size Now  Date      Time\n");
               printf (tot_line);
            }
            printf ("%8lu %3u%% %8lu  %2d %-.3s %02d %02d:%02d:%02d",  
                     direntry.org_size, 
                     size_factor, direntry.size_now, 
                     day, &month_list[month*3], 
                     (day && month) ?  (year+80) % 100 : 0,
                     hours, min, sec);
               tot_org_siz += direntry.org_size;
               tot_siz_now += direntry.size_now;
   
            if (direntry.cmt_size) {
               expl_comment++;
               printf ("C");
            } else
               printf (" ");
   
            if (direntry.deleted) {
               expl_deleted++;
               printf ("D");
            }  else
               printf (" ");
            if (list_deleted)
               printf (" ");
            if (show_name)
               printf ("%-*s ", longest, this_zoo);
            printf ("%s%s", 
                     whichname,
                     (direntry.dir_crc == 0) ? "" : (expl_star++, "*")
                   );
            if (bad_pack)
               printf (" (V%d.%d)", direntry.major_ver, direntry.minor_ver);
            printf ("\n");
         }
         first_time = 0;
   
         /* if verbose listing requested show any comment.  f overrrides v */
         if (verb_list && !fast)
            show_comment (&direntry, zoo_file, 0, NULL);
      }
   
      fseek (zoo_file, direntry.next, 0); /* ..seek to next dir entry */
   } /* end while */
   
   givesummary:
   
   if (!show_name) {
      if (fast) {
         if (file_count) {
            if (del_count)
               printf ("\n-----\n");
            else
               fputchar ('\n');
         }
         if (del_count)
            printf ("%d deleted.\n", del_count);
      }
      if (!fast && file_count) {
         tot_sf = cfactor (tot_org_siz, tot_siz_now);
         printf (tot_line);
      
         printf (tot_fmt, tot_org_siz, tot_sf, tot_siz_now, file_count,
            (file_count > 1) ? "s" : "");
      /* printf (tot_fmt, file_count, tot_org_siz, tot_sf, tot_siz_now); */
         
         if (del_count || expl_ver || expl_deleted || expl_comment)
            printf (dashes);
      }
   
      if (!fast) {
         if (del_count) {
            if (expl_deleted)
               printf ("D: deleted file.\n");
            else {
               if (del_count == 1)
                  printf ("There is 1 deleted file.\n");
               else
                  printf ("There are %d deleted files.\n", del_count);
            }
         }
      }
      if (expl_comment && !fast && !verb_list) 
         printf ("C: file has attached comment.\n");
      if (expl_ver && !fast)
         printf ("V: minimum version of Zoo needed to extract this file.\n");
      if (expl_star && !fast)
         printf ("*: directory entry may be corrupted.\n");
      if (!file_count)
         printf ("Zoo:  %s", no_match);
      
      if (!entrycount && !fiz_ofs)
         printf ("(The archive is empty.)\n");
   } /* end if (show_name) */
loop_end:            /* jump here on badly structured archive */
   fclose (zoo_file);
} /* end for */

if (show_name) {
   if (file_count) {
      tot_sf = cfactor (tot_org_siz, tot_siz_now);
      printf (tot_line);
      printf (tot_fmt, tot_org_siz, tot_sf, tot_siz_now, file_count);
   /* printf (tot_fmt, file_count, tot_org_siz, tot_sf, tot_siz_now); */
   } 
}

if (!file_count)
   exit (1);            /* Consider it an error if there were no files */
} /* zoolist() */

