xref: /minix/minix/commands/dosread/dosread.c (revision 83ee113e)
1 /* dos{dir|read|write} - {list|read|write} MS-DOS disks	 Author: M. Huisjes */
2 
3 /* Dosdir - list MS-DOS directories. doswrite - write stdin to DOS-file
4  * dosread - read DOS-file to stdout
5  *
6  * Author: Michiel Huisjes.
7  *
8  * Usage: dos... [-lra] drive [file/dir]
9  *	  l: Give long listing.
10  *	  r: List recursively.
11  *	  a: Set ASCII bit.
12  */
13 
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>
27 
28 
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 \
36 			        ))
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;
46 };
47 
48 typedef struct dir_entry DIRECTORY;
49 
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 
56 #define LAST_CLUSTER12	0xFFF
57 #define LAST_CLUSTER	0xFFFF
58 #define FREE		0x000
59 #define BAD		0xFF0
60 #define BAD16		0xFFF0
61 
62 typedef int BOOL;
63 
64 #define TRUE	1
65 #define FALSE	0
66 
67 #define DOS_TIME	315532800L	/* 1970 - 1980 */
68 
69 #define READ			0
70 #define WRITE			1
71 
72 #define FIND	3
73 #define LABEL	4
74 #define ENTRY	5
75 #define find_entry(d, e, p)	directory(d, e, FIND, p)
76 #define list_dir(d, e, f)	(void) directory(d, e, f, NULL)
77 #define label()			directory(root, root_entries, LABEL, NULL)
78 #define new_entry(d, e)		directory(d, e, ENTRY, NULL)
79 
80 #define is_dir(d)		((d)->d_attribute & SUB_DIR)
81 
82 #define STD_OUT			1
83 
84 char	*cmnd;
85 
86 static int disk;	/* File descriptor for disk I/O */
87 
88 static DIRECTORY root[MAX_ROOT_ENTRIES];
89 static DIRECTORY save_entry;
90 static char drive[] = "/dev/dosX";
91 #define DRIVE_NR	(sizeof (drive) - 2)
92 static char null[MAX_CLUSTER_SIZE], *device = drive, path[128];
93 static long data_start;
94 static long mark;	/* offset of directory entry to be written */
95 static unsigned short total_clusters, cluster_size, root_entries, sub_entries;
96 static unsigned long fat_size;
97 
98 static BOOL Rflag, Lflag, Aflag, dos_read, dos_write, dos_dir, fat_16 = 0;
99 static BOOL big_endian;
100 
101 /* maximum size of a cooked 12bit FAT. Also Size of 16bit FAT cache
102  * if not enough memory for whole FAT
103  */
104 #define COOKED_SIZE		8192
105 /* raw FAT. Only used for 12bit FAT to make conversion easier
106  */
107 static unsigned char	*raw_fat;
108 /* Cooked FAT. May be only part of the FAT for 16 bit FATs
109  */
110 static unsigned short	*cooked_fat;
111 /* lowest and highest entry in fat cache
112  */
113 static unsigned short	fat_low = USHRT_MAX,
114 			fat_high = 0;
115 static BOOL		fat_dirty = FALSE;
116 static unsigned int	cache_size;
117 
118 
119 /* Prototypes. */
120 void usage(char *prog_name);
121 unsigned c2u2(unsigned char *ucarray);
122 unsigned long c4u4(unsigned char *ucarray);
123 void determine(void);
124 int main(int argc, char *argv []);
125 DIRECTORY *directory(DIRECTORY *dir, int entries, BOOL function, char
126 	*pathname);
127 void extract(DIRECTORY *entry);
128 void make_file(DIRECTORY *dir_ptr, int entries, char *name);
129 void fill_date(DIRECTORY *entry);
130 char *make_name(DIRECTORY *dir_ptr, int dir_fl);
131 int fill(char *buffer, size_t size);
132 void xmodes(int mode);
133 void show(DIRECTORY *dir_ptr, char *name);
134 void free_blocks(void);
135 DIRECTORY *read_cluster(unsigned int cluster);
136 unsigned short free_cluster(BOOL leave_fl);
137 void link_fat(unsigned int cl_1, unsigned int cl_2);
138 unsigned short next_cluster(unsigned int cl_no);
139 char *slash(char *str);
140 void add_path(char *file, BOOL slash_fl);
141 void disk_io(BOOL op, unsigned long seek, void *address, unsigned
142 	bytes);
143 void flush_fat(void);
144 void read_fat(unsigned int cl_no);
145 BOOL free_range(unsigned short *first, unsigned short *last);
146 long lmin(long a, long b);
147 
148 
149 void usage(prog_name)
150 register char *prog_name;
151 {
152   fprintf (stderr, "Usage: %s [%s\n", prog_name,
153 	     (dos_dir ? "-lr] drive [dir]" : "-a] drive file"));
154   exit(1);
155 }
156 
157 unsigned c2u2(ucarray)
158 unsigned char *ucarray;
159 {
160   return ucarray[0] + (ucarray[1] << 8);	/* parens vital */
161 }
162 
163 unsigned long c4u4(ucarray)
164 unsigned char *ucarray;
165 {
166   return ucarray[0] + ((unsigned long) ucarray[1] << 8) +
167 		      ((unsigned long) ucarray[2] << 16) +
168 		      ((unsigned long) ucarray[3] << 24);
169 }
170 
171 void determine()
172 {
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;
199 
200   big_endian = !(*(unsigned char *)&endiantest);
201 
202   /* Read Bios-Parameterblock */
203   disk_io(READ, 0L, &boot, sizeof boot);
204   disk_io(READ, 0x1FEL, &boot_magic, sizeof boot_magic);
205 
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);
215 
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
220    */
221   hiddensec = c2u2(boot.chiddensec);
222   if (hiddensec == 0) hiddensec = c2u2 (boot.dos4hidd2);
223 
224   /* Safety checking */
225   if (boot_magic != 0xAA55) {
226 	fprintf (stderr, "%s: magic != 0xAA55\n", cmnd);
227 	++errcount;
228   }
229 
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;
241   }
242   if (bytepers == 0) {
243 	fprintf (stderr, "%s: bytes per sector == 0\n", cmnd);
244 	++errcount;
245   }
246   if (boot.secpclus == 0) {
247 	fprintf (stderr, "%s: sectors per cluster == 0\n", cmnd);
248 	++errcount;
249   }
250   if (boot.fats != 2 && dos_write) {
251 	fprintf (stderr, "%s: fats != 2\n", cmnd);
252 	++errcount;
253   }
254   if (reservsec != 1) {
255 	fprintf (stderr, "%s: reserved != 1\n", cmnd);
256 	++errcount;
257   }
258   if (errcount != 0) {
259 	fprintf (stderr, "%s: Can't handle disk\n", cmnd);
260 	exit(2);
261   }
262 
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;
276 
277   /* Further safety checking */
278   if (cluster_size > MAX_CLUSTER_SIZE) {
279 	fprintf (stderr, "%s: cluster size too big\n", cmnd);
280 	++errcount;
281   }
282 
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;
288   }
289   if (errcount != 0) {
290 	fprintf (stderr, "%s: Can't handle disk\n", cmnd);
291 	exit(2);
292   }
293 }
294 
295 int main(argc, argv)
296 int argc;
297 register char *argv[];
298 {
299   register char *arg_ptr = slash(argv[0]);
300   DIRECTORY *entry;
301   short idx = 1;
302   char dev_nr = '0';
303 
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);
314   }
315 
316   if (argc == 1) usage(argv[0]);
317 
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]);
330 		}
331 	}
332 	idx++;
333   }
334   if (idx == argc) usage(argv[0]);
335 
336   if (strlen(argv[idx]) > 1) {
337 	device = argv[idx++];
338 
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.
342 	 */
343 	if (strchr(device, '/') == NULL && chdir("/dev") < 0) {
344 		perror("/dev");
345 		exit(1);
346 	}
347   } else {
348 	if ((dev_nr = toupper (*argv[idx++])) < 'A' || dev_nr > 'Z')
349 		usage(argv[0]);
350 
351 	device[DRIVE_NR] = dev_nr;
352   }
353 
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);
358   }
359   determine();
360   disk_io(READ, ROOTADDR, root, DIR_SIZE * root_entries);
361 
362   if (dos_dir && Lflag) {
363 	entry = label();
364 	printf ("Volume in drive %c ", dev_nr);
365 	if (entry == NULL)
366 		printf("has no label.\n\n");
367 	else
368 		printf ("is %.11s\n\n", entry->d_name);
369   }
370   if (argv[idx] == NULL) {
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);
377   }
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 '/' */
382 
383   add_path(argv[idx], FALSE);
384   add_path("/", FALSE);
385 
386   if (dos_dir && Lflag) printf ( "Directory %s:\n", path);
387 
388   entry = find_entry(root, root_entries, argv[idx]);
389 
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 != NULL) {
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);
403 	}
404 	add_path(NULL, TRUE);
405 
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]);
410   }
411 
412   (void) close(disk);
413   fflush (stdout);
414   exit(0);
415   return(0);
416 }
417 
418 
419 /* General directory search routine.
420  *
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.
428  *
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
434  *
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).
440  */
441 DIRECTORY *directory(dir, entries, function, pathname)
442 DIRECTORY *dir;
443 int entries;
444 int function;
445 register char *pathname;
446 {
447   register DIRECTORY *dir_ptr = dir;
448   DIRECTORY *mem = NULL;
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;
454 
455   if (function == FIND) {
456 	while (*pathname != '/' && *pathname != '.' && *pathname &&
457 	       i < 8) {
458 		file_name[i++] = *pathname++;
459 	}
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++;
466 		}
467 	}
468 	while (*pathname != '/' && *pathname) pathname++;
469 	file_name[i] = '\0';
470   }
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);
476 	}
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;
486 			}
487 			continue;
488 		}
489 		if (type == NOT_USED) break;
490 		if (dir_ptr->d_attribute & 0x08) {
491 			if (function == LABEL) return dir_ptr;
492 			continue;
493 		}
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);
506 				}
507 			} else if (*pathname == '\0' && dos_read) {
508 				fflush (stdout);
509 				fprintf (stderr, "%s: %s is a directory.\n", cmnd, path);
510 				exit(1);
511 			}
512 			if (*pathname) {
513 				dir_ptr = find_entry(dir_ptr,
514 					 sub_entries, pathname + 1);
515 			}
516 			if (mem) {
517 				if (dir_ptr) {
518 					memcpy((char *)&save_entry, (char *)dir_ptr, DIR_SIZE);
519 					dir_ptr = &save_entry;
520 				}
521 				free( (void *) mem);
522 			}
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(NULL, FALSE);
532 			}
533 		}
534 	}
535 	if (mem) free( (void *) mem);
536   } while (cl_no != LAST_CLUSTER && mem);
537 
538   switch (function) {
539       case FIND:
540 	if (dos_write && *pathname == '\0') return NULL;
541 	fflush (stdout);
542 	fprintf (stderr, "%s: Cannot find `%s'.\n", cmnd, file_name);
543 	exit(1);
544       case LABEL:
545 	return NULL;
546       case ENTRY:
547 	if (!mem) {
548 		fflush (stdout);
549 		fprintf (stderr, "%s: No entries left in root directory.\n", cmnd);
550 		exit(1);
551 	}
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);
556 
557 	return new_entry(dir, entries);
558       case FALSE:
559 	if (Rflag) {
560 		printf ("\n");
561 		list_dir(dir, entries, TRUE);
562 	}
563   }
564   return NULL;
565 }
566 
567 void extract(entry)
568 register DIRECTORY *entry;
569 {
570   register unsigned short cl_no = entry->d_cluster;
571   char buffer[MAX_CLUSTER_SIZE];
572   int rest, i;
573 
574   if (entry->d_size == 0)	/* Empty file */
575 	return;
576 
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;
580 
581 	if (Aflag) {
582 		for (i = 0; i < rest; i ++) {
583 			if (buffer [i] != '\r') putchar (buffer [i]);
584 		}
585 		if (ferror (stdout)) {
586 			fprintf (stderr, "%s: cannot write to stdout: %s\n",
587 				 cmnd, strerror (errno));
588 			exit (1);
589 		}
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);
595 		}
596 	}
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);
604 	}
605   } while (entry->d_size && cl_no != LAST_CLUSTER);
606 
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);
612 }
613 
614 
615 /* Minimum of two long values
616  */
617 long lmin (a, b)
618 long a, b;
619 {
620 	if (a < b) return a;
621 	else return b;
622 }
623 
624 
625 void make_file(dir_ptr, entries, name)
626 DIRECTORY *dir_ptr;
627 int entries;
628 char *name;
629 {
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;
638 
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++;
645 
646   for (i = 0; i < 10; i++) entry->d_reserved[i] = '\0';
647   entry->d_attribute = '\0';
648 
649   entry->d_cluster = 0;
650 
651   while (free_range (&first_cluster, &last_cluster)) {
652 	do {
653 		unsigned short	nr_clus;
654 
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);
662 
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;
669 			}
670 			first_cluster ++;
671 		}
672 
673 		size += r;
674 	} while (first_cluster <= last_cluster);
675   }
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);
682 
683   if (fat_dirty) flush_fat ();
684 
685 }
686 
687 
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)
693 
694 unsigned short mon_len[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
695 
696 void fill_date(entry)
697 DIRECTORY *entry;
698 {
699   register long cur_time = time(NULL) - DOS_TIME;
700   unsigned short year = 0, month = 1, day, hour, minutes, seconds;
701   int i;
702   long tmp;
703 
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++;
711   }
712 
713   day = (unsigned short) (cur_time / SEC_DAY);
714   cur_time -= (long) day *SEC_DAY;
715 
716   hour = (unsigned short) (cur_time / SEC_HOUR);
717   cur_time -= (long) hour *SEC_HOUR;
718 
719   minutes = (unsigned short) (cur_time / SEC_MIN);
720   cur_time -= (long) minutes *SEC_MIN;
721 
722   seconds = (unsigned short) cur_time;
723 
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++];
729   }
730   day++;
731 
732   entry->d_date = (year << 9) | (month << 5) | day;
733   entry->d_time = (hour << 11) | (minutes << 5) | seconds;
734 }
735 
736 char *make_name(dir_ptr, dir_fl)
737 register DIRECTORY *dir_ptr;
738 short dir_fl;
739 {
740   static char name_buf[14];
741   register char *ptr = name_buf;
742   short i;
743 
744   for (i = 0; i < 8; i++) *ptr++ = dir_ptr->d_name[i];
745 
746   while (*--ptr == ' ');
747   assert (ptr >= name_buf);
748 
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++;
755   }
756   if (dir_fl) *ptr++ = '/';
757   *ptr = '\0';
758 
759   return name_buf;
760 }
761 
762 
763 int fill(buffer, size)
764 register char *buffer;
765 size_t	size;
766 {
767   static BOOL nl_mark = FALSE;
768   char *last = &buffer[size];
769   char *begin = buffer;
770   register int c;
771 
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;
784 		}
785 	}
786   }
787 
788   return (buffer - begin);
789 }
790 
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 */
796 
797 char *month[] = {
798 	 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
799 	 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
800 };
801 
802 void xmodes(mode)
803 int mode;
804 {
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' : '-');
808 }
809 
810 void show(dir_ptr, name)
811 DIRECTORY *dir_ptr;
812 char *name;
813 {
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;
819 
820   while (*name && *name != '/') bname[i++] = *name++;
821   bname[i] = '\0';
822   if (!Lflag) {
823 	printf ( "%s\n", bname);
824 	return;
825   }
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);
838 }
839 
840 void free_blocks()
841 {
842   register unsigned short cl_no;
843   long nr_free = 0;
844   long nr_bad = 0;
845 
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;
850 	}
851   }
852 
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);
856 }
857 
858 
859 DIRECTORY *read_cluster(cluster)
860 register unsigned int cluster;
861 {
862   register DIRECTORY *sub_dir;
863 
864   if ((sub_dir = malloc(cluster_size)) == NULL) {
865 	fprintf (stderr, "%s: Cannot set break!\n", cmnd);
866 	exit(1);
867   }
868   disk_io(READ, clus_add(cluster), sub_dir, cluster_size);
869 
870   return sub_dir;
871 }
872 
873 static unsigned short cl_index = 2;
874 
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.
878  *
879  * Warning: Assumes that all of the range is used before the next call
880  *	to free_range or free_cluster.
881  */
882 BOOL free_range (first, last)
883 unsigned short *first, *last;
884 {
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;
893 }
894 
895 
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
901  *
902  * Warning: Assumes that the cluster is used before the next call
903  *	to free_range or free_cluster.
904  */
905 unsigned short free_cluster(leave_fl)
906 BOOL leave_fl;
907 {
908   while (cl_index < total_clusters && next_cluster(cl_index) != FREE)
909 	cl_index++;
910 
911   if (leave_fl && cl_index >= total_clusters) {
912 	fprintf (stderr, "%s: Diskette full. File not added.\n", cmnd);
913 	exit(1);
914   }
915   return cl_index++;
916 }
917 
918 
919 /* read a portion of the fat containing |cl_no| into the cache
920  */
921 void read_fat (cl_no)
922   unsigned int cl_no;
923 {
924 
925   if (!cooked_fat) {
926   	/* Read the fat for the first time. We have to allocate all the
927   	 * buffers
928   	 */
929   	if (fat_16) {
930 		/* FAT consists of little endian shorts. Easy to convert
931 		 */
932 		if ((cooked_fat = malloc (fat_size)) == NULL) {
933 			/* Oops, FAT doesn't fit into memory, just read
934 			 * a chunk
935 			 */
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);
940 			}
941 			cache_size = COOKED_SIZE / 2;
942 		} else {
943 			cache_size = fat_size / 2;
944 		}
945 	} else {
946 		/* 12 bit FAT. Difficult encoding, but small. Keep
947 		 * both raw FAT and cooked version in memory.
948 		 */
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);
954 		}
955 		cache_size = total_clusters;
956 	}
957   }
958   fat_low = cl_no / cache_size * cache_size;
959   fat_high = fat_low + cache_size - 1;
960 
961   if (!fat_16) {
962   	unsigned short	*cp;
963   	unsigned char	*rp;
964   	unsigned short	i;
965 
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 ++;
978 	}
979   } else {
980 
981 	assert (sizeof (short) == 2);
982 	assert (CHAR_BIT == 8);		/* just in case */
983 
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;
989 
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);
994 		}
995 	}
996   }
997 }
998 
999 
1000 /* flush the fat cache out to disk
1001  */
1002 void flush_fat ()
1003 {
1004   if (fat_16) {
1005 	if (big_endian) {
1006 		unsigned short	*cp;
1007 		unsigned char	*rp;
1008 		unsigned short	i;
1009 
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;
1015 		}
1016 	}
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;
1023 
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);
1031 	}
1032 	disk_io (WRITE, FAT_START, raw_fat, fat_size);
1033 	disk_io (WRITE, FAT_START + fat_size, raw_fat, fat_size);
1034   }
1035 }
1036 
1037 
1038 /* make cl_2 the successor of cl_1
1039  */
1040 void link_fat(cl_1, cl_2)
1041 unsigned int cl_1;
1042 unsigned int cl_2;
1043 {
1044   if (cl_1 < fat_low || cl_1 > fat_high) {
1045   	if (fat_dirty) flush_fat ();
1046   	read_fat (cl_1);
1047   }
1048   cooked_fat [cl_1 - fat_low] = cl_2;
1049   fat_dirty = TRUE;
1050 }
1051 
1052 
1053 unsigned short next_cluster(cl_no)
1054 register unsigned int cl_no;
1055 {
1056   if (cl_no < fat_low || cl_no > fat_high) {
1057   	if (fat_dirty) flush_fat ();
1058   	read_fat (cl_no);
1059   }
1060   return cooked_fat [cl_no - fat_low];
1061 }
1062 
1063 char *slash(str)
1064 register char *str;
1065 {
1066   register char *result = str;
1067 
1068   while (*str)
1069 	if (*str++ == '/') result = str;
1070 
1071   return result;
1072 }
1073 
1074 void add_path(file, slash_fl)
1075 char *file;
1076 BOOL slash_fl;
1077 {
1078   register char *ptr = path;
1079 
1080   while (*ptr) ptr++;
1081 
1082   if (file == NULL) {
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);
1091 }
1092 
1093 
1094 void disk_io(op, seek, address, bytes)
1095 register BOOL op;
1096 unsigned long seek;
1097 void *address;
1098 register unsigned bytes;
1099 {
1100   unsigned int r;
1101 
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);
1106   }
1107   if (op == READ)
1108 	r = read(disk, (char *) address, bytes);
1109   else {
1110 	r = write(disk, (char *) address, bytes);
1111   }
1112 
1113   if (r != bytes) {
1114   	fprintf (stderr, "%s: read error: %s\n", cmnd, strerror (errno));
1115   	exit (1);
1116   }
1117 }
1118