source: trunk/minix/commands/ibm/dosread.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 27.7 KB
Line 
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 ))
37struct 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
48typedef 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#define NIL_DIR ((DIRECTORY *) 0)
56
57#define LAST_CLUSTER12 0xFFF
58#define LAST_CLUSTER 0xFFFF
59#define FREE 0x000
60#define BAD 0xFF0
61#define BAD16 0xFFF0
62
63typedef int BOOL;
64
65#define TRUE 1
66#define FALSE 0
67#define NIL_PTR ((char *) 0)
68
69#define DOS_TIME 315532800L /* 1970 - 1980 */
70
71#define READ 0
72#define WRITE 1
73
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)
81
82#define is_dir(d) ((d)->d_attribute & SUB_DIR)
83
84#define STD_OUT 1
85
86char *cmnd;
87
88static int disk; /* File descriptor for disk I/O */
89
90static DIRECTORY root[MAX_ROOT_ENTRIES];
91static DIRECTORY save_entry;
92static char drive[] = "/dev/dosX";
93#define DRIVE_NR (sizeof (drive) - 2)
94static char null[MAX_CLUSTER_SIZE], *device = drive, path[128];
95static long data_start;
96static long mark; /* offset of directory entry to be written */
97static unsigned short total_clusters, cluster_size, root_entries, sub_entries;
98static unsigned long fat_size;
99
100static BOOL Rflag, Lflag, Aflag, dos_read, dos_write, dos_dir, fat_16 = 0;
101static BOOL big_endian;
102
103/* maximum size of a cooked 12bit FAT. Also Size of 16bit FAT cache
104 * if not enough memory for whole FAT
105 */
106#define COOKED_SIZE 8192
107/* raw FAT. Only used for 12bit FAT to make conversion easier
108 */
109static unsigned char *raw_fat;
110/* Cooked FAT. May be only part of the FAT for 16 bit FATs
111 */
112static unsigned short *cooked_fat;
113/* lowest and highest entry in fat cache
114 */
115static unsigned short fat_low = USHRT_MAX,
116 fat_high = 0;
117static BOOL fat_dirty = FALSE;
118static unsigned int cache_size;
119
120
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));
147
148
149void usage(prog_name)
150register 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
157unsigned c2u2(ucarray)
158unsigned char *ucarray;
159{
160 return ucarray[0] + (ucarray[1] << 8); /* parens vital */
161}
162
163unsigned long c4u4(ucarray)
164unsigned 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
171void 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
295int main(argc, argv)
296int argc;
297register 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 == NIL_DIR)
366 printf("has no label.\n\n");
367 else
368 printf ("is %.11s\n\n", entry->d_name);
369 }
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);
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 != 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);
403 }
404 add_path(NIL_PTR, 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 */
441DIRECTORY *directory(dir, entries, function, pathname)
442DIRECTORY *dir;
443int entries;
444int function;
445register char *pathname;
446{
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;
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(NIL_PTR, 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 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);
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
567void extract(entry)
568register 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 */
617long lmin (a, b)
618long a, b;
619{
620 if (a < b) return a;
621 else return b;
622}
623
624
625void make_file(dir_ptr, entries, name)
626DIRECTORY *dir_ptr;
627int entries;
628char *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);
677done:
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
694unsigned short mon_len[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
695
696void fill_date(entry)
697DIRECTORY *entry;
698{
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;
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
736char *make_name(dir_ptr, dir_fl)
737register DIRECTORY *dir_ptr;
738short 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
763int fill(buffer, size)
764register char *buffer;
765size_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
797char *month[] = {
798 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
799 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
800};
801
802void xmodes(mode)
803int 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
810void show(dir_ptr, name)
811DIRECTORY *dir_ptr;
812char *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
840void 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
859DIRECTORY *read_cluster(cluster)
860register 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
873static 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 */
882BOOL free_range (first, last)
883unsigned 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 */
905unsigned short free_cluster(leave_fl)
906BOOL 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 */
921void 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 */
1002void 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 */
1040void link_fat(cl_1, cl_2)
1041unsigned int cl_1;
1042unsigned 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
1053unsigned short next_cluster(cl_no)
1054register 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
1063char *slash(str)
1064register char *str;
1065{
1066 register char *result = str;
1067
1068 while (*str)
1069 if (*str++ == '/') result = str;
1070
1071 return result;
1072}
1073
1074void add_path(file, slash_fl)
1075char *file;
1076BOOL slash_fl;
1077{
1078 register char *ptr = path;
1079
1080 while (*ptr) ptr++;
1081
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);
1091}
1092
1093
1094void disk_io(op, seek, address, bytes)
1095register BOOL op;
1096unsigned long seek;
1097void *address;
1098register 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
1119char dosread_c_rcs_id [] =
1120 "$Id: dosread.c,v 1.2 2005/07/13 10:02:14 beng Exp $";
Note: See TracBrowser for help on using the repository browser.