re-enable munmap().
[minix.git] / commands / ibm / dosread.c
blob6496980a93db81015a1f13fe21e727db49fb7cf7
1 /* dos{dir|read|write} - {list|read|write} MS-DOS disks Author: M. Huisjes */
3 /* Dosdir - list MS-DOS directories. doswrite - write stdin to DOS-file
4 * dosread - read DOS-file to stdout
6 * Author: Michiel Huisjes.
8 * Usage: dos... [-lra] drive [file/dir]
9 * l: Give long listing.
10 * r: List recursively.
11 * a: Set ASCII bit.
14 #include <assert.h>
15 #include <ctype.h>
16 #include <errno.h>
17 #include <limits.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <fcntl.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <time.h>
25 #include <sys/times.h>
26 #include <unistd.h>
29 #define MAX_CLUSTER_SIZE 4096
30 #define MAX_ROOT_ENTRIES 512
31 #define FAT_START 512L /* After bootsector */
32 #define ROOTADDR (FAT_START + 2L * fat_size)
33 #define clus_add(cl_no) ((long) (((long) cl_no - 2L) \
34 * (long) cluster_size \
35 + data_start \
37 struct dir_entry {
38 unsigned char d_name[8];
39 unsigned char d_ext[3];
40 unsigned char d_attribute;
41 unsigned char d_reserved[10];
42 unsigned short d_time;
43 unsigned short d_date;
44 unsigned short d_cluster;
45 unsigned long d_size;
48 typedef struct dir_entry DIRECTORY;
50 #define NOT_USED 0x00
51 #define ERASED 0xE5
52 #define DIR 0x2E
53 #define DIR_SIZE (sizeof (struct dir_entry))
54 #define SUB_DIR 0x10
55 #define NIL_DIR ((DIRECTORY *) 0)
57 #define LAST_CLUSTER12 0xFFF
58 #define LAST_CLUSTER 0xFFFF
59 #define FREE 0x000
60 #define BAD 0xFF0
61 #define BAD16 0xFFF0
63 typedef int BOOL;
65 #define TRUE 1
66 #define FALSE 0
67 #define NIL_PTR ((char *) 0)
69 #define DOS_TIME 315532800L /* 1970 - 1980 */
71 #define READ 0
72 #define WRITE 1
74 #define FIND 3
75 #define LABEL 4
76 #define ENTRY 5
77 #define find_entry(d, e, p) directory(d, e, FIND, p)
78 #define list_dir(d, e, f) (void) directory(d, e, f, NIL_PTR)
79 #define label() directory(root, root_entries, LABEL, NIL_PTR)
80 #define new_entry(d, e) directory(d, e, ENTRY, NIL_PTR)
82 #define is_dir(d) ((d)->d_attribute & SUB_DIR)
84 #define STD_OUT 1
86 char *cmnd;
88 static int disk; /* File descriptor for disk I/O */
90 static DIRECTORY root[MAX_ROOT_ENTRIES];
91 static DIRECTORY save_entry;
92 static char drive[] = "/dev/dosX";
93 #define DRIVE_NR (sizeof (drive) - 2)
94 static char null[MAX_CLUSTER_SIZE], *device = drive, path[128];
95 static long data_start;
96 static long mark; /* offset of directory entry to be written */
97 static unsigned short total_clusters, cluster_size, root_entries, sub_entries;
98 static unsigned long fat_size;
100 static BOOL Rflag, Lflag, Aflag, dos_read, dos_write, dos_dir, fat_16 = 0;
101 static BOOL big_endian;
103 /* maximum size of a cooked 12bit FAT. Also Size of 16bit FAT cache
104 * if not enough memory for whole FAT
106 #define COOKED_SIZE 8192
107 /* raw FAT. Only used for 12bit FAT to make conversion easier
109 static unsigned char *raw_fat;
110 /* Cooked FAT. May be only part of the FAT for 16 bit FATs
112 static unsigned short *cooked_fat;
113 /* lowest and highest entry in fat cache
115 static unsigned short fat_low = USHRT_MAX,
116 fat_high = 0;
117 static BOOL fat_dirty = FALSE;
118 static unsigned int cache_size;
121 /* Prototypes. */
122 _PROTOTYPE(void usage, (char *prog_name) );
123 _PROTOTYPE(unsigned c2u2, (unsigned char *ucarray) );
124 _PROTOTYPE(unsigned long c4u4, (unsigned char *ucarray) );
125 _PROTOTYPE(void determine, (void));
126 _PROTOTYPE(int main, (int argc, char *argv []));
127 _PROTOTYPE(DIRECTORY *directory, (DIRECTORY *dir, int entries, BOOL function, char *pathname) );
128 _PROTOTYPE(void extract, (DIRECTORY *entry) );
129 _PROTOTYPE(void make_file, (DIRECTORY *dir_ptr, int entries, char *name) );
130 _PROTOTYPE(void fill_date, (DIRECTORY *entry) );
131 _PROTOTYPE(char *make_name, (DIRECTORY *dir_ptr, int dir_fl) );
132 _PROTOTYPE(int fill, (char *buffer, size_t size) );
133 _PROTOTYPE(void xmodes, (int mode) );
134 _PROTOTYPE(void show, (DIRECTORY *dir_ptr, char *name) );
135 _PROTOTYPE(void free_blocks, (void));
136 _PROTOTYPE(DIRECTORY *read_cluster, (unsigned int cluster) );
137 _PROTOTYPE(unsigned short free_cluster, (BOOL leave_fl) );
138 _PROTOTYPE(void link_fat, (unsigned int cl_1, unsigned int cl_2) );
139 _PROTOTYPE(unsigned short next_cluster, (unsigned int cl_no) );
140 _PROTOTYPE(char *slash, (char *str) );
141 _PROTOTYPE(void add_path, (char *file, BOOL slash_fl) );
142 _PROTOTYPE(void disk_io, (BOOL op, unsigned long seek, void *address, unsigned bytes) );
143 _PROTOTYPE(void flush_fat, (void));
144 _PROTOTYPE(void read_fat, (unsigned int cl_no));
145 _PROTOTYPE(BOOL free_range, (unsigned short *first, unsigned short *last));
146 _PROTOTYPE(long lmin, (long a, long b));
149 void usage(prog_name)
150 register char *prog_name;
152 fprintf (stderr, "Usage: %s [%s\n", prog_name,
153 (dos_dir ? "-lr] drive [dir]" : "-a] drive file"));
154 exit(1);
157 unsigned c2u2(ucarray)
158 unsigned char *ucarray;
160 return ucarray[0] + (ucarray[1] << 8); /* parens vital */
163 unsigned long c4u4(ucarray)
164 unsigned char *ucarray;
166 return ucarray[0] + ((unsigned long) ucarray[1] << 8) +
167 ((unsigned long) ucarray[2] << 16) +
168 ((unsigned long) ucarray[3] << 24);
171 void determine()
173 struct dosboot {
174 unsigned char cjump[2]; /* unsigneds avoid bugs */
175 unsigned char nop;
176 unsigned char name[8];
177 unsigned char cbytepers[2]; /* don't use shorts, etc */
178 unsigned char secpclus; /* to avoid struct member */
179 unsigned char creservsec[2]; /* alignment and byte */
180 unsigned char fats; /* order bugs */
181 unsigned char cdirents[2];
182 unsigned char ctotsec[2];
183 unsigned char media;
184 unsigned char csecpfat[2];
185 unsigned char csecptrack[2];
186 unsigned char cheads[2];
187 unsigned char chiddensec[2];
188 unsigned char dos4hidd2[2];
189 unsigned char dos4totsec[4];
190 /* Char fill[476]; */
191 } boot;
192 unsigned short boot_magic; /* last of boot block */
193 unsigned bytepers, reservsec, dirents;
194 unsigned secpfat, secptrack, heads, hiddensec;
195 unsigned long totsec;
196 unsigned char fat_info, fat_check;
197 unsigned short endiantest = 1;
198 int errcount = 0;
200 big_endian = !(*(unsigned char *)&endiantest);
202 /* Read Bios-Parameterblock */
203 disk_io(READ, 0L, &boot, sizeof boot);
204 disk_io(READ, 0x1FEL, &boot_magic, sizeof boot_magic);
206 /* Convert some arrays */
207 bytepers = c2u2(boot.cbytepers);
208 reservsec = c2u2(boot.creservsec);
209 dirents = c2u2(boot.cdirents);
210 totsec = c2u2(boot.ctotsec);
211 if (totsec == 0) totsec = c4u4(boot.dos4totsec);
212 secpfat = c2u2(boot.csecpfat);
213 secptrack = c2u2(boot.csecptrack);
214 heads = c2u2(boot.cheads);
216 /* The `hidden sectors' are the sectors before the partition.
217 * The calculation here is probably wrong (I think the dos4hidd2
218 * bytes are the msbs), but that doesn't matter, since the
219 * value isn't used anyway
221 hiddensec = c2u2(boot.chiddensec);
222 if (hiddensec == 0) hiddensec = c2u2 (boot.dos4hidd2);
224 /* Safety checking */
225 if (boot_magic != 0xAA55) {
226 fprintf (stderr, "%s: magic != 0xAA55\n", cmnd);
227 ++errcount;
230 /* Check sectors per track instead of inadequate media byte */
231 if (secptrack < 15 && /* assume > 15 hard disk & wini OK */
232 #ifdef SECT10 /* BIOS modified for 10 sec/track */
233 secptrack != 10 &&
234 #endif
235 #ifdef SECT8 /* BIOS modified for 8 sec/track */
236 secptrack != 8 &&
237 #endif
238 secptrack != 9) {
239 fprintf (stderr, "%s: %d sectors per track not supported\n", cmnd, secptrack);
240 ++errcount;
242 if (bytepers == 0) {
243 fprintf (stderr, "%s: bytes per sector == 0\n", cmnd);
244 ++errcount;
246 if (boot.secpclus == 0) {
247 fprintf (stderr, "%s: sectors per cluster == 0\n", cmnd);
248 ++errcount;
250 if (boot.fats != 2 && dos_write) {
251 fprintf (stderr, "%s: fats != 2\n", cmnd);
252 ++errcount;
254 if (reservsec != 1) {
255 fprintf (stderr, "%s: reserved != 1\n", cmnd);
256 ++errcount;
258 if (errcount != 0) {
259 fprintf (stderr, "%s: Can't handle disk\n", cmnd);
260 exit(2);
263 /* Calculate everything. */
264 if (boot.secpclus == 0) boot.secpclus = 1;
265 total_clusters =
266 (totsec - boot.fats * secpfat - reservsec -
267 dirents * 32L / bytepers ) / boot.secpclus + 2;
268 /* first 2 entries in FAT aren't used */
269 cluster_size = bytepers * boot.secpclus;
270 fat_size = (unsigned long) secpfat * (unsigned long) bytepers;
271 data_start = (long) bytepers + (long) boot.fats * fat_size
272 + (long) dirents *32L;
273 root_entries = dirents;
274 sub_entries = boot.secpclus * bytepers / 32;
275 if (total_clusters > 4096) fat_16 = 1;
277 /* Further safety checking */
278 if (cluster_size > MAX_CLUSTER_SIZE) {
279 fprintf (stderr, "%s: cluster size too big\n", cmnd);
280 ++errcount;
283 disk_io(READ, FAT_START, &fat_info, 1);
284 disk_io(READ, FAT_START + fat_size, &fat_check, 1);
285 if (fat_check != fat_info) {
286 fprintf (stderr, "%s: Disk type in FAT copy differs from disk type in FAT original.\n", cmnd);
287 ++errcount;
289 if (errcount != 0) {
290 fprintf (stderr, "%s: Can't handle disk\n", cmnd);
291 exit(2);
295 int main(argc, argv)
296 int argc;
297 register char *argv[];
299 register char *arg_ptr = slash(argv[0]);
300 DIRECTORY *entry;
301 short idx = 1;
302 char dev_nr = '0';
304 cmnd = arg_ptr; /* needed for error messages */
305 if (!strcmp(arg_ptr, "dosdir"))
306 dos_dir = TRUE;
307 else if (!strcmp(arg_ptr, "dosread"))
308 dos_read = TRUE;
309 else if (!strcmp(arg_ptr, "doswrite"))
310 dos_write = TRUE;
311 else {
312 fprintf (stderr, "%s: Program should be named dosread, doswrite or dosdir.\n", cmnd);
313 exit(1);
316 if (argc == 1) usage(argv[0]);
318 if (argv[1][0] == '-') {
319 for (arg_ptr = &argv[1][1]; *arg_ptr; arg_ptr++) {
320 if (*arg_ptr == 'l' && dos_dir) {
321 Lflag = TRUE;
322 } else if (*arg_ptr == 'r' && dos_dir) {
323 Rflag = TRUE;
324 } else if (*arg_ptr == 'a' && !dos_dir) {
325 assert ('\n' == 10);
326 assert ('\r' == 13);
327 Aflag = TRUE;
328 } else {
329 usage(argv[0]);
332 idx++;
334 if (idx == argc) usage(argv[0]);
336 if (strlen(argv[idx]) > 1) {
337 device = argv[idx++];
339 /* If the device does not contain a / we assume that it
340 * is the name of a device in /dev. Instead of prepending
341 * /dev/ we try to chdir there.
343 if (strchr(device, '/') == NULL && chdir("/dev") < 0) {
344 perror("/dev");
345 exit(1);
347 } else {
348 if ((dev_nr = toupper (*argv[idx++])) < 'A' || dev_nr > 'Z')
349 usage(argv[0]);
351 device[DRIVE_NR] = dev_nr;
354 if ((disk = open(device, dos_write ? O_RDWR : O_RDONLY)) < 0) {
355 fprintf (stderr, "%s: cannot open %s: %s\n",
356 cmnd, device, strerror (errno));
357 exit(1);
359 determine();
360 disk_io(READ, ROOTADDR, root, DIR_SIZE * root_entries);
362 if (dos_dir && Lflag) {
363 entry = label();
364 printf ("Volume in drive %c ", dev_nr);
365 if (entry == NIL_DIR)
366 printf("has no label.\n\n");
367 else
368 printf ("is %.11s\n\n", entry->d_name);
370 if (argv[idx] == NIL_PTR) {
371 if (!dos_dir) usage(argv[0]);
372 if (Lflag) printf ("Root directory:\n");
373 list_dir(root, root_entries, FALSE);
374 if (Lflag) free_blocks();
375 fflush (stdout);
376 exit(0);
378 for (arg_ptr = argv[idx]; *arg_ptr; arg_ptr++)
379 if (*arg_ptr == '\\') *arg_ptr = '/';
380 else *arg_ptr = toupper (*arg_ptr);
381 if (*--arg_ptr == '/') *arg_ptr = '\0'; /* skip trailing '/' */
383 add_path(argv[idx], FALSE);
384 add_path("/", FALSE);
386 if (dos_dir && Lflag) printf ( "Directory %s:\n", path);
388 entry = find_entry(root, root_entries, argv[idx]);
390 if (dos_dir) {
391 list_dir(entry, sub_entries, FALSE);
392 if (Lflag) free_blocks();
393 } else if (dos_read)
394 extract(entry);
395 else {
396 if (entry != NIL_DIR) {
397 fflush (stdout);
398 if (is_dir(entry))
399 fprintf (stderr, "%s: %s is a directory.\n", cmnd, path);
400 else
401 fprintf (stderr, "%s: %s already exists.\n", cmnd, argv[idx]);
402 exit(1);
404 add_path(NIL_PTR, TRUE);
406 if (*path) make_file(find_entry(root, root_entries, path),
407 sub_entries, slash(argv[idx]));
408 else
409 make_file(root, root_entries, argv[idx]);
412 (void) close(disk);
413 fflush (stdout);
414 exit(0);
415 return(0);
419 /* General directory search routine.
421 * dir:
422 * Points to one or more directory entries
423 * entries:
424 * number of entries
425 * if entries == root_entries, dir points to the entire
426 * root directory. Otherwise it points to a single directory
427 * entry describing the directory to be searched.
429 * function:
430 * FIND ... find pathname relative to directory dir.
431 * LABEL ... find first label entry in dir.
432 * ENTRY ... create a new empty entry.
433 * FALSE ... list directory
435 * pathname:
436 * name of the file to be found or directory to be listed.
437 * must be in upper case, pathname components must be
438 * separated by slashes, but can be longer than than
439 * 8+3 characters (The rest is ignored).
441 DIRECTORY *directory(dir, entries, function, pathname)
442 DIRECTORY *dir;
443 int entries;
444 int function;
445 register char *pathname;
447 register DIRECTORY *dir_ptr = dir;
448 DIRECTORY *mem = NIL_DIR;
449 unsigned short cl_no = dir->d_cluster;
450 unsigned short type, last = 0;
451 char file_name[14];
452 char *name;
453 int i = 0;
455 if (function == FIND) {
456 while (*pathname != '/' && *pathname != '.' && *pathname &&
457 i < 8) {
458 file_name[i++] = *pathname++;
460 if (*pathname == '.') {
461 int j = 0;
462 file_name[i++] = *pathname++;
463 while (*pathname != '/' && *pathname != '.' && *pathname &&
464 j++ < 3) {
465 file_name[i++] = *pathname++;
468 while (*pathname != '/' && *pathname) pathname++;
469 file_name[i] = '\0';
471 do {
472 if (entries != root_entries) {
473 mem = dir_ptr = read_cluster(cl_no);
474 last = cl_no;
475 cl_no = next_cluster(cl_no);
477 for (i = 0; i < entries; i++, dir_ptr++) {
478 type = dir_ptr->d_name[0] & 0x0FF;
479 if (function == ENTRY) {
480 if (type == NOT_USED || type == ERASED) {
481 if (!mem)
482 mark = ROOTADDR + (long) i *(long) DIR_SIZE;
483 else
484 mark = clus_add(last) + (long) i *(long) DIR_SIZE;
485 return dir_ptr;
487 continue;
489 if (type == NOT_USED) break;
490 if (dir_ptr->d_attribute & 0x08) {
491 if (function == LABEL) return dir_ptr;
492 continue;
494 if (type == DIR || type == ERASED || function == LABEL)
495 continue;
496 type = is_dir(dir_ptr);
497 name = make_name(dir_ptr,
498 (function == FIND) ? FALSE : type);
499 if (function == FIND) {
500 if (strcmp(file_name, name) != 0) continue;
501 if (!type) {
502 if (dos_dir || *pathname) {
503 fflush (stdout);
504 fprintf (stderr, "%s: Not a directory: %s\n", cmnd, file_name);
505 exit(1);
507 } else if (*pathname == '\0' && dos_read) {
508 fflush (stdout);
509 fprintf (stderr, "%s: %s is a directory.\n", cmnd, path);
510 exit(1);
512 if (*pathname) {
513 dir_ptr = find_entry(dir_ptr,
514 sub_entries, pathname + 1);
516 if (mem) {
517 if (dir_ptr) {
518 memcpy((char *)&save_entry, (char *)dir_ptr, DIR_SIZE);
519 dir_ptr = &save_entry;
521 free( (void *) mem);
523 return dir_ptr;
524 } else {
525 if (function == FALSE) {
526 show(dir_ptr, name);
527 } else if (type) { /* Recursive */
528 printf ( "Directory %s%s:\n", path, name);
529 add_path(name, FALSE);
530 list_dir(dir_ptr, sub_entries, FALSE);
531 add_path(NIL_PTR, FALSE);
535 if (mem) free( (void *) mem);
536 } while (cl_no != LAST_CLUSTER && mem);
538 switch (function) {
539 case FIND:
540 if (dos_write && *pathname == '\0') return NIL_DIR;
541 fflush (stdout);
542 fprintf (stderr, "%s: Cannot find `%s'.\n", cmnd, file_name);
543 exit(1);
544 case LABEL:
545 return NIL_DIR;
546 case ENTRY:
547 if (!mem) {
548 fflush (stdout);
549 fprintf (stderr, "%s: No entries left in root directory.\n", cmnd);
550 exit(1);
552 cl_no = free_cluster(TRUE);
553 link_fat(last, cl_no);
554 link_fat(cl_no, LAST_CLUSTER);
555 disk_io(WRITE, clus_add(cl_no), null, cluster_size);
557 return new_entry(dir, entries);
558 case FALSE:
559 if (Rflag) {
560 printf ("\n");
561 list_dir(dir, entries, TRUE);
564 return NULL;
567 void extract(entry)
568 register DIRECTORY *entry;
570 register unsigned short cl_no = entry->d_cluster;
571 char buffer[MAX_CLUSTER_SIZE];
572 int rest, i;
574 if (entry->d_size == 0) /* Empty file */
575 return;
577 do {
578 disk_io(READ, clus_add(cl_no), buffer, cluster_size);
579 rest = (entry->d_size > (long) cluster_size) ? cluster_size : (short) entry->d_size;
581 if (Aflag) {
582 for (i = 0; i < rest; i ++) {
583 if (buffer [i] != '\r') putchar (buffer [i]);
585 if (ferror (stdout)) {
586 fprintf (stderr, "%s: cannot write to stdout: %s\n",
587 cmnd, strerror (errno));
588 exit (1);
590 } else {
591 if (fwrite (buffer, 1, rest, stdout) != rest) {
592 fprintf (stderr, "%s: cannot write to stdout: %s\n",
593 cmnd, strerror (errno));
594 exit (1);
597 entry->d_size -= (long) rest;
598 cl_no = next_cluster(cl_no);
599 if (cl_no == BAD16) {
600 fflush (stdout);
601 fprintf (stderr, "%s: reserved cluster value %x encountered.\n",
602 cmnd, cl_no);
603 exit (1);
605 } while (entry->d_size && cl_no != LAST_CLUSTER);
607 if (cl_no != LAST_CLUSTER)
608 fprintf (stderr, "%s: Too many clusters allocated for file.\n", cmnd);
609 else if (entry->d_size != 0)
610 fprintf (stderr, "%s: Premature EOF: %ld bytes left.\n", cmnd,
611 entry->d_size);
615 /* Minimum of two long values
617 long lmin (a, b)
618 long a, b;
620 if (a < b) return a;
621 else return b;
625 void make_file(dir_ptr, entries, name)
626 DIRECTORY *dir_ptr;
627 int entries;
628 char *name;
630 register DIRECTORY *entry = new_entry(dir_ptr, entries);
631 register char *ptr;
632 char buffer[MAX_CLUSTER_SIZE];
633 unsigned short cl_no = 0;
634 int i, r;
635 long size = 0L;
636 unsigned short first_cluster, last_cluster;
637 long chunk;
639 memset (&entry->d_name[0], ' ', 11); /* clear entry */
640 for (i = 0, ptr = name; i < 8 && *ptr != '.' && *ptr; i++)
641 entry->d_name[i] = *ptr++;
642 while (*ptr != '.' && *ptr) ptr++;
643 if (*ptr == '.') ptr++;
644 for (i = 0; i < 3 && *ptr != '.' && *ptr; i++) entry->d_ext[i] = *ptr++;
646 for (i = 0; i < 10; i++) entry->d_reserved[i] = '\0';
647 entry->d_attribute = '\0';
649 entry->d_cluster = 0;
651 while (free_range (&first_cluster, &last_cluster)) {
652 do {
653 unsigned short nr_clus;
655 chunk = lmin ((long) (last_cluster - first_cluster + 1) *
656 cluster_size,
657 (long) MAX_CLUSTER_SIZE);
658 r = fill(buffer, chunk);
659 if (r == 0) goto done;
660 nr_clus = (r + cluster_size - 1) / cluster_size;
661 disk_io(WRITE, clus_add(first_cluster), buffer, r);
663 for (i = 0; i < nr_clus; i ++) {
664 if (entry->d_cluster == 0)
665 cl_no = entry->d_cluster = first_cluster;
666 else {
667 link_fat(cl_no, first_cluster);
668 cl_no = first_cluster;
670 first_cluster ++;
673 size += r;
674 } while (first_cluster <= last_cluster);
676 fprintf (stderr, "%s: disk full. File truncated\n", cmnd);
677 done:
678 if (entry->d_cluster != 0) link_fat(cl_no, LAST_CLUSTER);
679 entry->d_size = size;
680 fill_date(entry);
681 disk_io(WRITE, mark, entry, DIR_SIZE);
683 if (fat_dirty) flush_fat ();
688 #define SEC_MIN 60L
689 #define SEC_HOUR (60L * SEC_MIN)
690 #define SEC_DAY (24L * SEC_HOUR)
691 #define SEC_YEAR (365L * SEC_DAY)
692 #define SEC_LYEAR (366L * SEC_DAY)
694 unsigned short mon_len[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
696 void fill_date(entry)
697 DIRECTORY *entry;
699 register long cur_time = time((long *) 0) - DOS_TIME;
700 unsigned short year = 0, month = 1, day, hour, minutes, seconds;
701 int i;
702 long tmp;
704 if (cur_time < 0) /* Date not set on booting ... */
705 cur_time = 0;
706 for (;;) {
707 tmp = (year % 4 == 0) ? SEC_LYEAR : SEC_YEAR;
708 if (cur_time < tmp) break;
709 cur_time -= tmp;
710 year++;
713 day = (unsigned short) (cur_time / SEC_DAY);
714 cur_time -= (long) day *SEC_DAY;
716 hour = (unsigned short) (cur_time / SEC_HOUR);
717 cur_time -= (long) hour *SEC_HOUR;
719 minutes = (unsigned short) (cur_time / SEC_MIN);
720 cur_time -= (long) minutes *SEC_MIN;
722 seconds = (unsigned short) cur_time;
724 mon_len[1] = (year % 4 == 0) ? 29 : 28;
725 i = 0;
726 while (day >= mon_len[i]) {
727 month++;
728 day -= mon_len[i++];
730 day++;
732 entry->d_date = (year << 9) | (month << 5) | day;
733 entry->d_time = (hour << 11) | (minutes << 5) | seconds;
736 char *make_name(dir_ptr, dir_fl)
737 register DIRECTORY *dir_ptr;
738 short dir_fl;
740 static char name_buf[14];
741 register char *ptr = name_buf;
742 short i;
744 for (i = 0; i < 8; i++) *ptr++ = dir_ptr->d_name[i];
746 while (*--ptr == ' ');
747 assert (ptr >= name_buf);
749 ptr++;
750 if (dir_ptr->d_ext[0] != ' ') {
751 *ptr++ = '.';
752 for (i = 0; i < 3; i++) *ptr++ = dir_ptr->d_ext[i];
753 while (*--ptr == ' ');
754 ptr++;
756 if (dir_fl) *ptr++ = '/';
757 *ptr = '\0';
759 return name_buf;
763 int fill(buffer, size)
764 register char *buffer;
765 size_t size;
767 static BOOL nl_mark = FALSE;
768 char *last = &buffer[size];
769 char *begin = buffer;
770 register int c;
772 while (buffer < last) {
773 if (nl_mark) {
774 *buffer ++ = '\n';
775 nl_mark = FALSE;
776 } else {
777 c = getchar();
778 if (c == EOF) break;
779 if (Aflag && c == '\n') {
780 *buffer ++ = '\r';
781 nl_mark = TRUE;
782 } else {
783 *buffer++ = c;
788 return (buffer - begin);
791 #define HOUR 0xF800 /* Upper 5 bits */
792 #define MIN 0x07E0 /* Middle 6 bits */
793 #define YEAR 0xFE00 /* Upper 7 bits */
794 #define MONTH 0x01E0 /* Mid 4 bits */
795 #define DAY 0x01F /* Lowest 5 bits */
797 char *month[] = {
798 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
799 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
802 void xmodes(mode)
803 int mode;
805 printf ( "\t%c%c%c%c%c", (mode & SUB_DIR) ? 'd' : '-',
806 (mode & 02) ? 'h' : '-', (mode & 04) ? 's' : '-',
807 (mode & 01) ? '-' : 'w', (mode & 0x20) ? 'a' : '-');
810 void show(dir_ptr, name)
811 DIRECTORY *dir_ptr;
812 char *name;
814 register unsigned short e_date = dir_ptr->d_date;
815 register unsigned short e_time = dir_ptr->d_time;
816 unsigned short next;
817 char bname[20];
818 short i = 0;
820 while (*name && *name != '/') bname[i++] = *name++;
821 bname[i] = '\0';
822 if (!Lflag) {
823 printf ( "%s\n", bname);
824 return;
826 xmodes( (int) dir_ptr->d_attribute);
827 printf ( "\t%s%s", bname, strlen(bname) < 8 ? "\t\t" : "\t");
828 i = 1;
829 if (is_dir(dir_ptr)) {
830 next = dir_ptr->d_cluster;
831 while ((next = next_cluster(next)) != LAST_CLUSTER) i++;
832 printf ("%8ld", (long) i * (long) cluster_size);
833 } else
834 printf ("%8ld", dir_ptr->d_size);
835 printf (" %02d:%02d %2d %s %d\n", ((e_time & HOUR) >> 11),
836 ((e_time & MIN) >> 5), (e_date & DAY),
837 month[((e_date & MONTH) >> 5) - 1], ((e_date & YEAR) >> 9) + 1980);
840 void free_blocks()
842 register unsigned short cl_no;
843 long nr_free = 0;
844 long nr_bad = 0;
846 for (cl_no = 2; cl_no < total_clusters; cl_no++) {
847 switch (next_cluster(cl_no)) {
848 case FREE: nr_free++; break;
849 case BAD16: nr_bad++; break;
853 printf ("Free space: %ld bytes.\n", nr_free * (long) cluster_size);
854 if (nr_bad != 0)
855 printf ("Bad sectors: %ld bytes.\n", nr_bad * (long) cluster_size);
859 DIRECTORY *read_cluster(cluster)
860 register unsigned int cluster;
862 register DIRECTORY *sub_dir;
864 if ((sub_dir = malloc(cluster_size)) == NULL) {
865 fprintf (stderr, "%s: Cannot set break!\n", cmnd);
866 exit(1);
868 disk_io(READ, clus_add(cluster), sub_dir, cluster_size);
870 return sub_dir;
873 static unsigned short cl_index = 2;
875 /* find a range of consecutive free clusters. Return TRUE if found
876 * and return the first and last cluster in the |*first| and |*last|.
877 * If no free clusters are left, return FALSE.
879 * Warning: Assumes that all of the range is used before the next call
880 * to free_range or free_cluster.
882 BOOL free_range (first, last)
883 unsigned short *first, *last;
885 while (cl_index < total_clusters && next_cluster(cl_index) != FREE)
886 cl_index++;
887 if (cl_index >= total_clusters) return FALSE;
888 *first = cl_index;
889 while (cl_index < total_clusters && next_cluster(cl_index) == FREE)
890 cl_index++;
891 *last = cl_index - 1;
892 return TRUE;
896 /* find a free cluster.
897 * Return the number of the free cluster or a number > |total_clusters|
898 * if none is found.
899 * If |leave_fl| is TRUE, the the program will be terminated if
900 * no free cluster can be found
902 * Warning: Assumes that the cluster is used before the next call
903 * to free_range or free_cluster.
905 unsigned short free_cluster(leave_fl)
906 BOOL leave_fl;
908 while (cl_index < total_clusters && next_cluster(cl_index) != FREE)
909 cl_index++;
911 if (leave_fl && cl_index >= total_clusters) {
912 fprintf (stderr, "%s: Diskette full. File not added.\n", cmnd);
913 exit(1);
915 return cl_index++;
919 /* read a portion of the fat containing |cl_no| into the cache
921 void read_fat (cl_no)
922 unsigned int cl_no;
925 if (!cooked_fat) {
926 /* Read the fat for the first time. We have to allocate all the
927 * buffers
929 if (fat_16) {
930 /* FAT consists of little endian shorts. Easy to convert
932 if ((cooked_fat = malloc (fat_size)) == NULL) {
933 /* Oops, FAT doesn't fit into memory, just read
934 * a chunk
936 if ((cooked_fat = malloc (COOKED_SIZE)) == NULL) {
937 fprintf (stderr, "%s: not enough memory for FAT cache. Use chmem\n",
938 cmnd);
939 exit (1);
941 cache_size = COOKED_SIZE / 2;
942 } else {
943 cache_size = fat_size / 2;
945 } else {
946 /* 12 bit FAT. Difficult encoding, but small. Keep
947 * both raw FAT and cooked version in memory.
949 if ((cooked_fat = malloc (total_clusters * sizeof (short))) == NULL ||
950 (raw_fat = malloc (fat_size)) == NULL) {
951 fprintf (stderr, "%s: not enough memory for FAT cache. Use chmem\n",
952 cmnd);
953 exit (1);
955 cache_size = total_clusters;
958 fat_low = cl_no / cache_size * cache_size;
959 fat_high = fat_low + cache_size - 1;
961 if (!fat_16) {
962 unsigned short *cp;
963 unsigned char *rp;
964 unsigned short i;
966 disk_io (READ, FAT_START, raw_fat, fat_size);
967 for (rp = raw_fat, cp = cooked_fat, i = 0;
968 i < cache_size;
969 rp += 3, i += 2) {
970 *cp = *rp + ((*(rp + 1) & 0x0f) << 8);
971 if (*cp == BAD) *cp = BAD16;
972 else if (*cp == LAST_CLUSTER12) *cp = LAST_CLUSTER;
973 cp ++;
974 *cp = ((*(rp + 1) & 0xf0) >> 4) + (*(rp + 2) << 4);
975 if (*cp == BAD) *cp = BAD16;
976 else if (*cp == LAST_CLUSTER12) *cp = LAST_CLUSTER;
977 cp ++;
979 } else {
981 assert (sizeof (short) == 2);
982 assert (CHAR_BIT == 8); /* just in case */
984 disk_io (READ, FAT_START + fat_low * 2, (void *)cooked_fat, cache_size * 2);
985 if (big_endian) {
986 unsigned short *cp;
987 unsigned char *rp;
988 unsigned short i;
990 for (i = 0, rp = (unsigned char *)cooked_fat /* sic */, cp = cooked_fat;
991 i < cache_size;
992 rp += 2, cp ++, i ++) {
993 *cp = c2u2 (rp);
1000 /* flush the fat cache out to disk
1002 void flush_fat ()
1004 if (fat_16) {
1005 if (big_endian) {
1006 unsigned short *cp;
1007 unsigned char *rp;
1008 unsigned short i;
1010 for (i = 0, rp = (unsigned char *)cooked_fat /* sic */, cp = cooked_fat;
1011 i < cache_size;
1012 rp += 2, cp ++, i ++) {
1013 *rp = *cp;
1014 *(rp + 1) = *cp >> 8;
1017 disk_io (WRITE, FAT_START + fat_low * 2, (void *)cooked_fat, cache_size * 2);
1018 disk_io (WRITE, FAT_START + fat_size + fat_low * 2, (void *)cooked_fat, cache_size * 2);
1019 } else {
1020 unsigned short *cp;
1021 unsigned char *rp;
1022 unsigned short i;
1024 for (rp = raw_fat, cp = cooked_fat, i = 0;
1025 i < cache_size;
1026 rp += 3, cp += 2, i += 2) {
1027 *rp = *cp;
1028 *(rp + 1) = ((*cp & 0xf00) >> 8) |
1029 ((*(cp + 1) & 0x00f) << 4);
1030 *(rp + 2) = ((*(cp + 1) & 0xff0) >> 4);
1032 disk_io (WRITE, FAT_START, raw_fat, fat_size);
1033 disk_io (WRITE, FAT_START + fat_size, raw_fat, fat_size);
1038 /* make cl_2 the successor of cl_1
1040 void link_fat(cl_1, cl_2)
1041 unsigned int cl_1;
1042 unsigned int cl_2;
1044 if (cl_1 < fat_low || cl_1 > fat_high) {
1045 if (fat_dirty) flush_fat ();
1046 read_fat (cl_1);
1048 cooked_fat [cl_1 - fat_low] = cl_2;
1049 fat_dirty = TRUE;
1053 unsigned short next_cluster(cl_no)
1054 register unsigned int cl_no;
1056 if (cl_no < fat_low || cl_no > fat_high) {
1057 if (fat_dirty) flush_fat ();
1058 read_fat (cl_no);
1060 return cooked_fat [cl_no - fat_low];
1063 char *slash(str)
1064 register char *str;
1066 register char *result = str;
1068 while (*str)
1069 if (*str++ == '/') result = str;
1071 return result;
1074 void add_path(file, slash_fl)
1075 char *file;
1076 BOOL slash_fl;
1078 register char *ptr = path;
1080 while (*ptr) ptr++;
1082 if (file == NIL_PTR) {
1083 if (ptr != path) ptr--;
1084 if (ptr != path) do {
1085 ptr--;
1086 } while (*ptr != '/' && ptr != path);
1087 if (ptr != path && !slash_fl) *ptr++ = '/';
1088 *ptr = '\0';
1089 } else
1090 strcpy (ptr, file);
1094 void disk_io(op, seek, address, bytes)
1095 register BOOL op;
1096 unsigned long seek;
1097 void *address;
1098 register unsigned bytes;
1100 unsigned int r;
1102 if (lseek(disk, seek, SEEK_SET) < 0L) {
1103 fflush (stdout);
1104 fprintf (stderr, "%s: Bad lseek: %s\n", cmnd, strerror (errno));
1105 exit(1);
1107 if (op == READ)
1108 r = read(disk, (char *) address, bytes);
1109 else {
1110 r = write(disk, (char *) address, bytes);
1113 if (r != bytes) {
1114 fprintf (stderr, "%s: read error: %s\n", cmnd, strerror (errno));
1115 exit (1);
1119 char dosread_c_rcs_id [] =
1120 "$Id$";