#ifndef LINT
/* derived from: zoolist.c 2.27 88/08/15 11:03:16 */
static char sccsid[]="$Source: /usr/home/dhesi/zoo/RCS/zoolist.c,v $\n\
$Id: zoolist.c,v 1.4 91/07/09 01:54:16 dhesi Exp $";
#endif /* LINT */

/*
If TRACE_LIST is defined, any list command may be followed
by 'D' to show verbose information about each directory
entry in the archive.	Do not define both TRACE_LIST and
TRACE_IO else a symbol conflict will occur and in any case
duplicate information will be dumped.
*/

/* #define TRACE_LIST */

/*
Copyright (C) 1986, 1987 Rahul Dhesi -- All rights reserved
(C) Copyright 1988 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 "zooio.h"
#include "various.h"
#include "zoofns.h"
#include <stdio.h>

#ifdef TRACE_LIST
void show_dir PARMS ((struct direntry *direntry));
static int trace_list = 0;
#endif /* TRACE_LIST */

static char tot_fmt[] = "%8lu %3u%% %8lu  %4d file";
static char tot_line[] =
	/* "------------  --------  ---  --------  --------- --------\n"; */
	"--------  --- --------  --------- --------\n";

static char dbl_percent[] = "Archive %s:  %s";

extern int quiet; 			/* assumed initialized to zero */

void show_comment PARMS((struct direntry *, ZOOFILE, int, char *));
int ver_too_high PARMS((struct zoo_header *));
int needed PARMS((char *, struct direntry *, struct zoo_header *));
void printtz PARMS((int));

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 ZOOFILE 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;
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 */
long fiz_ofs = 0; 				/* offset where to start */
long dat_ofs = 0; 				/* ... data offset of file data */
int verb_list = 0;				/* if verbose listing needed */
int show_name = 0;				/* if archive name to be included in listing */
int show_crc = 0; 				/* if crc should be listed */
int zoocount = 1; 				/* number of archives to list */
int biglist = 0;					/* multiarchive listing */
int one_col = 0;					/* one column listing requested */
int showdir = 0;					/* show directory name in fast listing */
int longest;						/* length of longest archive name */
int talking;						/* opposite of quiet */
int column = 0;					/* for column printing */
int first_ever = 1;				/* first time ever -- very special case */
int neednl = 0;					/* whether to print a newline */
int need_acmt = 0;				/* show archive comment */
int show_gen = 0; 				/* show generation count */
int genson = 1;					/* enable/disable generations */
#ifdef FATTR
int show_mode = 0;				/* show file protection */
#endif
int first_dir = 1;				/* if first direntry -- to adjust dat_ofs */

while (*option) {
	switch (*option) {
		case 'a': show_name++; break;
#ifdef TRACE_LIST
		case 'D': trace_list++; break;
#endif /* TRACE_LIST */
		case 'd': list_deleted++; break;
		case 'f': fast++; break;
		case 'g': show_gen++; break;
		case '/': showdir++; break;
		case 'A':
		case 'v': need_acmt++; break;
		case 'V': need_acmt++; /* fall through */
		case 'c': verb_list++; break;
		case 'C': show_crc++; break;
		case 'l': break;
		case 'L': biglist++; zoocount = argc; break;
#ifdef FATTR
		case 'm': show_mode++; break;
#endif
		case '1': one_col++; break;
		case '+': genson = 1; break;
		case '-': genson = 0; break;
		/* following code same as in zooext.c */
		case '@':   /* if @m,n specified, fiz_ofs = m, dat_ofs = n */
			{
				char *comma_pos;
				++option;
				comma_pos = strchr(option, ',');
				if (comma_pos != NULL) {
					dat_ofs = calc_ofs (comma_pos + 1);
					*comma_pos = '\0';
				}
				fiz_ofs = calc_ofs(option);
				goto no_more;
			}
		case 'q': quiet++; break;
		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');
}

talking = !quiet; 				/* for convenience */

#ifdef WILDCARD
	/* 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,   (char *) NULL,".","..", &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 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 = del_count =
		expl_deleted = expl_comment = expl_ver = expl_star = 0;

	if (talking)
		column = 0; 					/* if quiet, names will run together */

	first_time = 1;

#ifndef WILDCARD
	/* Add default extension if none supplied */
	if (strchr (nameptr (this_zoo), EXT_CH) == NULL)
		this_zoo = newcat (this_zoo, EXT_DFLT);
#endif

	zoo_file = zooopen (this_zoo, Z_READ);

	if (zoo_file == NOFILE) {
		prterror ('e', could_not_open, this_zoo);
		continue;
	} else if (!show_name && talking)
		printf ("\nArchive %s:\n", this_zoo);

if (fiz_ofs != 0L) {                /* if offset specified, start there */
	prterror ('m', start_ofs, fiz_ofs, dat_ofs);
	zooseek (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;
	}
#if 0
	if (talking && (!show_name || verb_list || need_acmt))
#else
	if (need_acmt && talking)
#endif
	{
		void show_acmt PARMS ((struct zoo_header *, ZOOFILE, int));
		show_acmt (&zoo_header, zoo_file, 0);     /* show archive comment */
	}

	/* Seek to the beginning of the first directory entry */
	if (zooseek (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 ('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 = zootell (zoo_file)) != -1L)
				if (zooseek (zoo_file, 0L, 2) == 0)
					if ((zoolength = zootell (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 */
		/* first direntry read, change dat_ofs from abs. pos. to rel. offset */
		if (first_dir && dat_ofs != 0) {
			dat_ofs -= direntry.offset;
			first_dir = 0;
		}
		direntry.next += dat_ofs;				/* allow for user-specified offset */
		if (direntry.comment != 0L)
			direntry.comment += dat_ofs;		/* so show_comment finds it */

		if (direntry.deleted)
			++del_count;

#ifdef TRACE_LIST
		if (trace_list)
			show_dir (&direntry);
#endif /* TRACE_LIST */

		/* Into `whichname' put the filename to display. Use long filename if
		it exists, else use short filename.  */
			strcpy (whichname, fullpath (&direntry));
			if (zoo_header.vdata & VFL_ON)
				add_version (whichname, &direntry); /* add version suffix */
#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, &direntry, &zoo_header))) {
			/* if generations forced off, then strip added version field */
			if (!genson) { /* HORRENDOUSLY INEFFICIENT AND REPETITIOUS */
				char *ver_pos;
				ver_pos = findlast (whichname, VER_DISPLAY);
				if (ver_pos != NULL)
					*ver_pos = '\0';
			}

			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;

			/* Alignment in columns is a horrendously complex undertaking. */

			if (fast) {
				int space_left;
				int namelen;
				int next_col;
#if 0
				if ( (quiet && !first_ever || !first_time) && one_col)
					fputchar ('\n');
				first_ever = 0;
#endif
				/* If we are showing directories, whichname already contains the
				full pathname string.  Else we only use the filename as follows:
				long filename if possible, else short filename */
				if (!showdir) {
					strcpy (whichname,
						(direntry.namlen != 0) ? direntry.lfname : direntry.fname);
					if (genson && zoo_header.vdata & VFL_ON)
						add_version (whichname, &direntry); /* add version suffix */
				}
				namelen = strlen (whichname);

#define MARGIN 		78
#define COL_WIDTH 	16
#if 1
				/* if not enough space left, move to next line */
				if (!one_col && column != 0) {
					space_left = MARGIN - column;
					if (namelen > space_left) {
						neednl = 1;
						column = 0;
					}
				}
#endif
				if ( (quiet && !first_ever || !first_time) && (neednl || one_col))
					printf ("\n");
				first_ever = 0;
				neednl = 0;

				printf("%s", whichname);
				fflush (stdout);
				/* move to next column stop */
				column += namelen;
				next_col = ((column + (COL_WIDTH - 1)) / COL_WIDTH) * COL_WIDTH;
				if (next_col - column < 2)    /* need at least 2 spaces */
					next_col += COL_WIDTH;
				if (next_col > MARGIN) {
					neednl = 1;
					column = 0;
				} else {
					if (!one_col)
						printf ("%*s", (next_col - column), " ");
					column = next_col;
				}

			} else {
				if (talking && 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;
#ifdef GETTZ
				printtz ((int) direntry.tz);  /* show timezone */
#else
				printf (" ");
#endif

				if (show_crc)
					printf ("%04x ", direntry.file_crc);
				if (show_gen) {
					if (direntry.vflag & VFL_ON)
						printf ("%2dg ", direntry.vflag & VFL_GEN);
					else
						printf ("--g ");
				}

				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);

#ifdef FATTR
				if (show_mode) {
					if (direntry.fattr == 0)
						printf ("--- ");
					else if ((direntry.fattr >> 22) == 1)
						printf ("%03o ", direntry.fattr & 0x1ff);
					else
						printf ("??? ");
				}
#endif /* FATTR */

				/* new code to get around a common compiler bug */
				printf ("%s", whichname);
				if (direntry.dir_crc != 0) {
					expl_star++;
					printf ("*");
				}

				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, (char *) NULL);
		} /* end if (lots of conditions) */

		/* ..seek to next dir entry */
		zooseek (zoo_file, direntry.next, 0);
	} /* end while */

	givesummary:

	if (fast && talking) {
		if (file_count) {
			if (del_count || (show_gen && zoo_header.type > 0))
				printf ("\n-----\n");
			else
				fputchar ('\n');
		}
		if (del_count)
			printf ("%d deleted.\n", del_count);
		if (show_gen && zoo_header.type > 0) {
			printf ("Generation limit %u",
						zoo_header.vdata & VFL_GEN);
			if ((zoo_header.vdata & VFL_ON) == 0)
				printf (" (off).\n");
			else
				printf (".\n");
		}
	} /* end if (fast && talking) */

	if (talking && !show_name) {
		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);
			if (file_count > 1)
				printf ("s\n");
			else
				printf ("\n");

			if (del_count || expl_ver || expl_deleted || expl_comment ||
					expl_star || (show_gen && (zoo_header.type > 0)))
				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");
		if (show_gen && (zoo_header.type > 0) && !fast) {
			printf ("Archive generation limit is %u",
						zoo_header.vdata & VFL_GEN);
			if ((zoo_header.vdata & VFL_ON) == 0)
				printf (" (generations off).\n");
			else
				printf (".\n");
		}
	} /* end if (talking && !show_name) */
loop_end:				/* jump here on badly structured archive */
	zooclose (zoo_file);
} /* end for */

if (talking && 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);
		if (file_count > 1)
			printf ("s\n");
		else
			printf ("\n");
	}
} else if (fast && quiet)
	fputchar ('\n');


if (!file_count)
	zooexit (1);            /* Consider it an error if there were no files */
} /* zoolist() */

#ifdef GETTZ
void printtz (file_tz)
int file_tz;
{
	long gettz();
	int diff_tz;				/* timezone difference */
	if (file_tz == NO_TZ)   /* if no timezone stored ..*/
		printf ("   ");         /* .. just pad with blanks */
	else {
		diff_tz = (file_tz / 4) - (int) (gettz() / 3600);
		if (diff_tz == 0)
			printf ("   ");               /* print nothing if same */
		else if (diff_tz > 0)         /* else print signed difference */
			printf ("+%1d ", diff_tz);
		else
			printf ("-%1d ", -diff_tz);
	}
}
#endif

/*
FOLLOWING CODE IS FOR DEBUGGING ONLY.	IT IS COMPILED IN ONLY
IF THE SYMBOL TRACE_LIST IS DEFINED
*/

#ifdef TRACE_LIST
/* code copied from portable.c near end */
/* dump contents of directory entry */
void show_dir (direntry)
struct direntry *direntry;
{
	printf ("Directory entry for file [%s][%s]:\n",
				direntry->fname, direntry->lfname);
	printf ("tag = [%8lx] type = [%d] PM = [%d] Next = [%8lx] Offset = [%8lx]\n",
				direntry->zoo_tag, (int) direntry->type,
				(int) direntry->packing_method, direntry->next,
				direntry->offset);
	printf ("Orig size = [%ld] Size now = [%ld] dmaj_v.dmin_v = [%d.%d]\n",
			direntry->org_size, direntry->size_now,
			(int) direntry->major_ver, (int) direntry->minor_ver);
	printf ("Struc = [%d] DEL = [%d] comment_offset = [%8lx] cmt_size = [%d]\n",
			(int) direntry->struc, (int) direntry->deleted, direntry->comment,
			direntry->cmt_size);
	printf ("var_dir_len = [%d] TZ = [%d] dir_crc = [%4x]\n",
				direntry->var_dir_len, (int) direntry->tz, direntry->dir_crc);
	printf ("system_id = [%d]  dirlen = [%d]  namlen = [%d] fattr=[%24lx]\n",
		direntry->system_id, direntry->dirlen, direntry->namlen, direntry->fattr);
	printf ("vflag = [%4x] version_no = [%4x]\n",
				direntry->vflag, direntry->version_no);
	if (direntry->dirlen > 0)
		printf ("dirname = [%s]\n", direntry->dirname);
	printf ("---------\n");
}
#endif	/* TRACE_IO */
