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