[9] | 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 | #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 |
|
---|
| 63 | typedef 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 |
|
---|
| 86 | char *cmnd;
|
---|
| 87 |
|
---|
| 88 | static int disk; /* File descriptor for disk I/O */
|
---|
| 89 |
|
---|
| 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;
|
---|
| 99 |
|
---|
| 100 | static BOOL Rflag, Lflag, Aflag, dos_read, dos_write, dos_dir, fat_16 = 0;
|
---|
| 101 | static 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 | */
|
---|
| 109 | static unsigned char *raw_fat;
|
---|
| 110 | /* Cooked FAT. May be only part of the FAT for 16 bit FATs
|
---|
| 111 | */
|
---|
| 112 | static unsigned short *cooked_fat;
|
---|
| 113 | /* lowest and highest entry in fat cache
|
---|
| 114 | */
|
---|
| 115 | static unsigned short fat_low = USHRT_MAX,
|
---|
| 116 | fat_high = 0;
|
---|
| 117 | static BOOL fat_dirty = FALSE;
|
---|
| 118 | static 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 |
|
---|
| 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 == 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 | */
|
---|
| 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 = 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 |
|
---|
| 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((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 |
|
---|
| 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 == 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 |
|
---|
| 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 |
|
---|
| 1119 | char dosread_c_rcs_id [] =
|
---|
| 1120 | "$Id: dosread.c,v 1.2 2005/07/13 10:02:14 beng Exp $";
|
---|