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.
18 #include <sys/types.h>
25 #include <sys/times.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 \
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
;
48 typedef struct dir_entry DIRECTORY
;
53 #define DIR_SIZE (sizeof (struct dir_entry))
55 #define NIL_DIR ((DIRECTORY *) 0)
57 #define LAST_CLUSTER12 0xFFF
58 #define LAST_CLUSTER 0xFFFF
67 #define NIL_PTR ((char *) 0)
69 #define DOS_TIME 315532800L /* 1970 - 1980 */
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)
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
,
117 static BOOL fat_dirty
= FALSE
;
118 static unsigned int cache_size
;
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"));
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);
174 unsigned char cjump
[2]; /* unsigneds avoid bugs */
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];
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]; */
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;
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
);
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 */
235 #ifdef SECT8 /* BIOS modified for 8 sec/track */
239 fprintf (stderr
, "%s: %d sectors per track not supported\n", cmnd
, secptrack
);
243 fprintf (stderr
, "%s: bytes per sector == 0\n", cmnd
);
246 if (boot
.secpclus
== 0) {
247 fprintf (stderr
, "%s: sectors per cluster == 0\n", cmnd
);
250 if (boot
.fats
!= 2 && dos_write
) {
251 fprintf (stderr
, "%s: fats != 2\n", cmnd
);
254 if (reservsec
!= 1) {
255 fprintf (stderr
, "%s: reserved != 1\n", cmnd
);
259 fprintf (stderr
, "%s: Can't handle disk\n", cmnd
);
263 /* Calculate everything. */
264 if (boot
.secpclus
== 0) boot
.secpclus
= 1;
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
);
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
);
290 fprintf (stderr
, "%s: Can't handle disk\n", cmnd
);
297 register char *argv
[];
299 register char *arg_ptr
= slash(argv
[0]);
304 cmnd
= arg_ptr
; /* needed for error messages */
305 if (!strcmp(arg_ptr
, "dosdir"))
307 else if (!strcmp(arg_ptr
, "dosread"))
309 else if (!strcmp(arg_ptr
, "doswrite"))
312 fprintf (stderr
, "%s: Program should be named dosread, doswrite or dosdir.\n", cmnd
);
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
) {
322 } else if (*arg_ptr
== 'r' && dos_dir
) {
324 } else if (*arg_ptr
== 'a' && !dos_dir
) {
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) {
348 if ((dev_nr
= toupper (*argv
[idx
++])) < 'A' || dev_nr
> 'Z')
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
));
360 disk_io(READ
, ROOTADDR
, root
, DIR_SIZE
* root_entries
);
362 if (dos_dir
&& Lflag
) {
364 printf ("Volume in drive %c ", dev_nr
);
365 if (entry
== NIL_DIR
)
366 printf("has no label.\n\n");
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();
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
]);
391 list_dir(entry
, sub_entries
, FALSE
);
392 if (Lflag
) free_blocks();
396 if (entry
!= NIL_DIR
) {
399 fprintf (stderr
, "%s: %s is a directory.\n", cmnd
, path
);
401 fprintf (stderr
, "%s: %s already exists.\n", cmnd
, argv
[idx
]);
404 add_path(NIL_PTR
, TRUE
);
406 if (*path
) make_file(find_entry(root
, root_entries
, path
),
407 sub_entries
, slash(argv
[idx
]));
409 make_file(root
, root_entries
, argv
[idx
]);
419 /* General directory search routine.
422 * Points to one or more directory 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.
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
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
)
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;
455 if (function
== FIND
) {
456 while (*pathname
!= '/' && *pathname
!= '.' && *pathname
&&
458 file_name
[i
++] = *pathname
++;
460 if (*pathname
== '.') {
462 file_name
[i
++] = *pathname
++;
463 while (*pathname
!= '/' && *pathname
!= '.' && *pathname
&&
465 file_name
[i
++] = *pathname
++;
468 while (*pathname
!= '/' && *pathname
) pathname
++;
472 if (entries
!= root_entries
) {
473 mem
= dir_ptr
= read_cluster(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
) {
482 mark
= ROOTADDR
+ (long) i
*(long) DIR_SIZE
;
484 mark
= clus_add(last
) + (long) i
*(long) DIR_SIZE
;
489 if (type
== NOT_USED
) break;
490 if (dir_ptr
->d_attribute
& 0x08) {
491 if (function
== LABEL
) return dir_ptr
;
494 if (type
== DIR || type
== ERASED
|| function
== LABEL
)
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;
502 if (dos_dir
|| *pathname
) {
504 fprintf (stderr
, "%s: Not a directory: %s\n", cmnd
, file_name
);
507 } else if (*pathname
== '\0' && dos_read
) {
509 fprintf (stderr
, "%s: %s is a directory.\n", cmnd
, path
);
513 dir_ptr
= find_entry(dir_ptr
,
514 sub_entries
, pathname
+ 1);
518 memcpy((char *)&save_entry
, (char *)dir_ptr
, DIR_SIZE
);
519 dir_ptr
= &save_entry
;
525 if (function
== FALSE
) {
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
);
540 if (dos_write
&& *pathname
== '\0') return NIL_DIR
;
542 fprintf (stderr
, "%s: Cannot find `%s'.\n", cmnd
, file_name
);
549 fprintf (stderr
, "%s: No entries left in root directory.\n", cmnd
);
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
);
561 list_dir(dir
, entries
, TRUE
);
568 register DIRECTORY
*entry
;
570 register unsigned short cl_no
= entry
->d_cluster
;
571 char buffer
[MAX_CLUSTER_SIZE
];
574 if (entry
->d_size
== 0) /* Empty file */
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
;
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
));
591 if (fwrite (buffer
, 1, rest
, stdout
) != rest
) {
592 fprintf (stderr
, "%s: cannot write to stdout: %s\n",
593 cmnd
, strerror (errno
));
597 entry
->d_size
-= (long) rest
;
598 cl_no
= next_cluster(cl_no
);
599 if (cl_no
== BAD16
) {
601 fprintf (stderr
, "%s: reserved cluster value %x encountered.\n",
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
,
615 /* Minimum of two long values
625 void make_file(dir_ptr
, entries
, name
)
630 register DIRECTORY
*entry
= new_entry(dir_ptr
, entries
);
632 char buffer
[MAX_CLUSTER_SIZE
];
633 unsigned short cl_no
= 0;
636 unsigned short first_cluster
, last_cluster
;
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
)) {
653 unsigned short nr_clus
;
655 chunk
= lmin ((long) (last_cluster
- first_cluster
+ 1) *
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
;
667 link_fat(cl_no
, first_cluster
);
668 cl_no
= first_cluster
;
674 } while (first_cluster
<= last_cluster
);
676 fprintf (stderr
, "%s: disk full. File truncated\n", cmnd
);
678 if (entry
->d_cluster
!= 0) link_fat(cl_no
, LAST_CLUSTER
);
679 entry
->d_size
= size
;
681 disk_io(WRITE
, mark
, entry
, DIR_SIZE
);
683 if (fat_dirty
) flush_fat ();
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
)
699 register long cur_time
= time((long *) 0) - DOS_TIME
;
700 unsigned short year
= 0, month
= 1, day
, hour
, minutes
, seconds
;
704 if (cur_time
< 0) /* Date not set on booting ... */
707 tmp
= (year
% 4 == 0) ? SEC_LYEAR
: SEC_YEAR
;
708 if (cur_time
< tmp
) break;
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;
726 while (day
>= mon_len
[i
]) {
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
;
740 static char name_buf
[14];
741 register char *ptr
= name_buf
;
744 for (i
= 0; i
< 8; i
++) *ptr
++ = dir_ptr
->d_name
[i
];
746 while (*--ptr
== ' ');
747 assert (ptr
>= name_buf
);
750 if (dir_ptr
->d_ext
[0] != ' ') {
752 for (i
= 0; i
< 3; i
++) *ptr
++ = dir_ptr
->d_ext
[i
];
753 while (*--ptr
== ' ');
756 if (dir_fl
) *ptr
++ = '/';
763 int fill(buffer
, size
)
764 register char *buffer
;
767 static BOOL nl_mark
= FALSE
;
768 char *last
= &buffer
[size
];
769 char *begin
= buffer
;
772 while (buffer
< last
) {
779 if (Aflag
&& c
== '\n') {
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 */
798 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
799 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
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
)
814 register unsigned short e_date
= dir_ptr
->d_date
;
815 register unsigned short e_time
= dir_ptr
->d_time
;
820 while (*name
&& *name
!= '/') bname
[i
++] = *name
++;
823 printf ( "%s\n", bname
);
826 xmodes( (int) dir_ptr
->d_attribute
);
827 printf ( "\t%s%s", bname
, strlen(bname
) < 8 ? "\t\t" : "\t");
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
);
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);
842 register unsigned short cl_no
;
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
);
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
);
868 disk_io(READ
, clus_add(cluster
), sub_dir
, cluster_size
);
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
)
887 if (cl_index
>= total_clusters
) return FALSE
;
889 while (cl_index
< total_clusters
&& next_cluster(cl_index
) == FREE
)
891 *last
= cl_index
- 1;
896 /* find a free cluster.
897 * Return the number of the free cluster or a number > |total_clusters|
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
)
908 while (cl_index
< total_clusters
&& next_cluster(cl_index
) != FREE
)
911 if (leave_fl
&& cl_index
>= total_clusters
) {
912 fprintf (stderr
, "%s: Diskette full. File not added.\n", cmnd
);
919 /* read a portion of the fat containing |cl_no| into the cache
921 void read_fat (cl_no
)
926 /* Read the fat for the first time. We have to allocate all the
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
936 if ((cooked_fat
= malloc (COOKED_SIZE
)) == NULL
) {
937 fprintf (stderr
, "%s: not enough memory for FAT cache. Use chmem\n",
941 cache_size
= COOKED_SIZE
/ 2;
943 cache_size
= fat_size
/ 2;
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",
955 cache_size
= total_clusters
;
958 fat_low
= cl_no
/ cache_size
* cache_size
;
959 fat_high
= fat_low
+ cache_size
- 1;
966 disk_io (READ
, FAT_START
, raw_fat
, fat_size
);
967 for (rp
= raw_fat
, cp
= cooked_fat
, i
= 0;
970 *cp
= *rp
+ ((*(rp
+ 1) & 0x0f) << 8);
971 if (*cp
== BAD
) *cp
= BAD16
;
972 else if (*cp
== LAST_CLUSTER12
) *cp
= LAST_CLUSTER
;
974 *cp
= ((*(rp
+ 1) & 0xf0) >> 4) + (*(rp
+ 2) << 4);
975 if (*cp
== BAD
) *cp
= BAD16
;
976 else if (*cp
== LAST_CLUSTER12
) *cp
= LAST_CLUSTER
;
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);
990 for (i
= 0, rp
= (unsigned char *)cooked_fat
/* sic */, cp
= cooked_fat
;
992 rp
+= 2, cp
++, i
++) {
1000 /* flush the fat cache out to disk
1010 for (i
= 0, rp
= (unsigned char *)cooked_fat
/* sic */, cp
= cooked_fat
;
1012 rp
+= 2, cp
++, i
++) {
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);
1024 for (rp
= raw_fat
, cp
= cooked_fat
, i
= 0;
1026 rp
+= 3, cp
+= 2, i
+= 2) {
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
)
1044 if (cl_1
< fat_low
|| cl_1
> fat_high
) {
1045 if (fat_dirty
) flush_fat ();
1048 cooked_fat
[cl_1
- fat_low
] = cl_2
;
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 ();
1060 return cooked_fat
[cl_no
- fat_low
];
1066 register char *result
= str
;
1069 if (*str
++ == '/') result
= str
;
1074 void add_path(file
, slash_fl
)
1078 register char *ptr
= path
;
1082 if (file
== NIL_PTR
) {
1083 if (ptr
!= path
) ptr
--;
1084 if (ptr
!= path
) do {
1086 } while (*ptr
!= '/' && ptr
!= path
);
1087 if (ptr
!= path
&& !slash_fl
) *ptr
++ = '/';
1094 void disk_io(op
, seek
, address
, bytes
)
1098 register unsigned bytes
;
1102 if (lseek(disk
, seek
, SEEK_SET
) < 0L) {
1104 fprintf (stderr
, "%s: Bad lseek: %s\n", cmnd
, strerror (errno
));
1108 r
= read(disk
, (char *) address
, bytes
);
1110 r
= write(disk
, (char *) address
, bytes
);
1114 fprintf (stderr
, "%s: read error: %s\n", cmnd
, strerror (errno
));
1119 char dosread_c_rcs_id
[] =