aboutsummaryrefslogtreecommitdiff
path: root/src/listalf.c
blob: c4cd713d94561d82560b5414f53504b63e9ed8dd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <stdio.h>
#include <stdlib.h>
#include <f65.h>
#include "unalf.h"
#include "addrs.h"

static const char *monthnames[] = {
	"???",
	"Jan", "Feb", "Mar", "Apr",
	"May", "Jun", "Jul", "Aug",
	"Sep", "Oct", "Nov", "Dec",
	"???", "???", "???",
};

u16 getword(int offs) {
	return mem[offs] | (mem[offs + 1] << 8);
}

unsigned int getquad(int offs) {
	return getword(offs) | ((getword(offs + 2) << 16));
}

/* see Arcinfo for details */
void format_msdos_time(char *buf) {
	u16 t, hour, min;
	char ampm;

	t = getword(alf_hdr_time0);

	/* don't bother with seconds (arc doesn't print them either) */
	hour = t >> 11;
	min = (t >> 5) & 0x3f;

	/* midnight is 12:00a (unlike arc, which uses 0:00a). noon
		is 12:00p (same as arc). could just use a 24h clock like
		unzip does, but AM/PM are more intuitive to me. */
	ampm = (hour > 11 ? 'p' : 'a');
	hour %= 12;

	/* midnight and noon print as 12, not 0 */
	if(hour == 0) hour = 12;

	snprintf(buf, 7, "%2d:%02d%c", hour, min, ampm);
}

/* see Arcinfo for details */
void format_msdos_date(char *buf) {
	u16 d, year, month, day;

	d = getword(alf_hdr_date0);

	/* year actually ranges 1980 to 2107... */
	year = (d >> 9) + 1980;
	year %= 100; /* ...but only print last 2 digits */

	/* valid months range 1 to 12. values 0, 13, 14, 15 are possible
	   but invalid (print as ???) */
	month = (d >> 5) & 0x0f;

	/* valid day range is 1 to 31. 0 is invalid, print as-is. */
	day = d & 0x1f;

	snprintf(buf, 10, "%2d %3s %02d", day, monthnames[month], year);
}

/* small files may be "compressed" larger than the original, so this
   has to return a signed type. */
static int comp_percent(unsigned int orig, unsigned int comp) {
	if(orig == 0) return 0; /* no division by zero please */
	return 100 - (int)((float)comp / (float)orig * 100.0);
}

/* output similar to "arc v", except we omit the compression type column
   since there's only one type. also, don't call the checksum a CRC, it
   isn't. */
void list_alf(void) {
	unsigned int c = 0, orig_size, comp_size, total_osize = 0, total_csize = 0;
	char buf[100];
	extern char *out_filename;

	if(opts.verbose_list) {
		puts("Name          Length    Size now  Comp  Date       Time    CkSum");
		puts("============  ========  ========  ====  =========  ======  =====");
	}

	while(read_alf_header()) {
		c++;
		orig_size = getquad(alf_hdr_origsize0);
		comp_size = getquad(alf_hdr_compsize0);
		out_filename = (char *)(mem + alf_hdr_filename);

		if(!opts.verbose_list)
			fix_filename();

		total_osize += orig_size;
		total_csize += comp_size;

		if(opts.verbose_list) {
			printf("%-12s  ", out_filename);
			printf("%8d  ", orig_size);
			printf("%8d  ", comp_size);
			printf("%3d%%  ", comp_percent(orig_size, comp_size));
			format_msdos_date(buf);
			printf("%9s  ", buf);
			format_msdos_time(buf);
			printf("%6s  ", buf);
			printf(" %04x", getword(alf_hdr_cksum_l));
			putchar('\n');
		} else {
			if(file_wanted(out_filename))
				printf("%s\n", out_filename);
		}

		if(fseek(in_file, comp_size, SEEK_CUR) != 0) {
			fputs(self, stderr);
			perror(": fseek");
			exit(1);
		}
	}

	if(opts.verbose_list) {
		fputs("        ====  ========  ========  ====\nTotal   ", stdout);
		printf("%4d  ", c);
		printf("%8d  ", total_osize);
		printf("%8d  ", total_csize);
		printf("%3d%%  ", comp_percent(total_osize, total_csize));
		putchar('\n');
	}
}