1/* Copyright 1996-2005,2007-2009 Alain Knaff. 2 * This file is part of mtools. 3 * 4 * Mtools is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * Mtools is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with Mtools. If not, see <http://www.gnu.org/licenses/>. 16 * 17 */ 18#include "sysincludes.h" 19#include "mtools.h" 20#include "codepage.h" 21#include "mtoolsPaths.h" 22 23/* global variables */ 24/* they are not really harmful here, because there is only one configuration 25 * file per invocations */ 26 27#define MAX_LINE_LEN 256 28 29/* scanner */ 30static char buffer[MAX_LINE_LEN+1]; /* buffer for the whole line */ 31static char *pos; /* position in line */ 32static char *token; /* last scanned token */ 33static size_t token_length; /* length of the token */ 34static FILE *fp; /* file pointer for configuration file */ 35static int linenumber; /* current line number. Only used for printing 36 * error messages */ 37static int lastTokenLinenumber; /* line numnber for last token */ 38static const char *filename=NULL; /* current file name. Used for printing 39 * error messages, and for storing in 40 * the device definition (mtoolstest) */ 41static int file_nr=0; 42 43 44static int flag_mask; /* mask of currently set flags */ 45 46/* devices */ 47static int cur_devs; /* current number of defined devices */ 48static int cur_dev; /* device being filled in. If negative, none */ 49static int trusted=0; /* is the currently parsed device entry trusted? */ 50static int nr_dev; /* number of devices that the current table can hold */ 51struct device *devices; /* the device table */ 52static int token_nr; /* number of tokens in line */ 53 54static char default_drive='\0'; /* default drive */ 55 56/* "environment" variables */ 57unsigned int mtools_skip_check=1; // Foxconn modified pling 05/07/2009, 0->1 58unsigned int mtools_fat_compatibility=0; 59unsigned int mtools_ignore_short_case=0; 60unsigned int mtools_rate_0=0; 61unsigned int mtools_rate_any=0; 62unsigned int mtools_no_vfat=0; 63unsigned int mtools_numeric_tail=1; 64unsigned int mtools_dotted_dir=0; 65unsigned int mtools_twenty_four_hour_clock=1; 66unsigned int mtools_default_codepage=850; 67const char *mtools_date_string="yyyy-mm-dd"; 68char *country_string=0; 69 70typedef struct switches_l { 71 const char *name; 72 caddr_t address; 73 enum { 74 T_INT, 75 T_STRING, 76 T_UINT 77 } type; 78} switches_t; 79 80static switches_t global_switches[] = { 81 { "MTOOLS_LOWER_CASE", (caddr_t) & mtools_ignore_short_case, T_UINT }, 82 { "MTOOLS_FAT_COMPATIBILITY", (caddr_t) & mtools_fat_compatibility, T_UINT }, 83 { "MTOOLS_SKIP_CHECK", (caddr_t) & mtools_skip_check, T_UINT }, 84 { "MTOOLS_NO_VFAT", (caddr_t) & mtools_no_vfat, T_UINT }, 85 { "MTOOLS_RATE_0", (caddr_t) &mtools_rate_0, T_UINT }, 86 { "MTOOLS_RATE_ANY", (caddr_t) &mtools_rate_any, T_UINT }, 87 { "MTOOLS_NAME_NUMERIC_TAIL", (caddr_t) &mtools_numeric_tail, T_UINT }, 88 { "MTOOLS_DOTTED_DIR", (caddr_t) &mtools_dotted_dir, T_UINT }, 89 { "MTOOLS_TWENTY_FOUR_HOUR_CLOCK", 90 (caddr_t) &mtools_twenty_four_hour_clock, T_UINT }, 91 { "MTOOLS_DATE_STRING", 92 (caddr_t) &mtools_date_string, T_STRING }, 93 { "DEFAULT_CODEPAGE", (caddr_t) &mtools_default_codepage, T_UINT } 94}; 95 96typedef struct { 97 const char *name; 98 int flag; 99} flags_t; 100 101static flags_t openflags[] = { 102#ifdef O_SYNC 103 { "sync", O_SYNC }, 104#endif 105#ifdef O_NDELAY 106 { "nodelay", O_NDELAY }, 107#endif 108#ifdef O_EXCL 109 { "exclusive", O_EXCL }, 110#endif 111 { "none", 0 } /* hack for those compilers that choke on commas 112 * after the last element of an array */ 113}; 114 115static flags_t misc_flags[] = { 116#ifdef USE_XDF 117 { "use_xdf", USE_XDF_FLAG }, 118#endif 119 { "scsi", SCSI_FLAG }, 120 { "nolock", NOLOCK_FLAG }, 121 { "mformat_only", MFORMAT_ONLY_FLAG }, 122 { "filter", FILTER_FLAG }, 123 { "privileged", PRIV_FLAG }, 124 { "vold", VOLD_FLAG }, 125 { "remote", FLOPPYD_FLAG }, 126 { "swap", SWAP_FLAG }, 127}; 128 129static struct { 130 const char *name; 131 signed char fat_bits; 132 int tracks; 133 unsigned short heads; 134 unsigned short sectors; 135} default_formats[] = { 136 { "hd514", 12, 80, 2, 15 }, 137 { "high-density-5-1/4", 12, 80, 2, 15 }, 138 { "1.2m", 12, 80, 2, 15 }, 139 140 { "hd312", 12, 80, 2, 18 }, 141 { "high-density-3-1/2", 12, 80, 2, 18 }, 142 { "1.44m", 12, 80, 2, 18 }, 143 144 { "dd312", 12, 80, 2, 9 }, 145 { "double-density-3-1/2", 12, 80, 2, 9 }, 146 { "720k", 12, 80, 2, 9 }, 147 148 { "dd514", 12, 40, 2, 9 }, 149 { "double-density-5-1/4", 12, 40, 2, 9 }, 150 { "360k", 12, 40, 2, 9 }, 151 152 { "320k", 12, 40, 2, 8 }, 153 { "180k", 12, 40, 1, 9 }, 154 { "160k", 12, 40, 1, 8 } 155}; 156 157#define OFFS(x) ((caddr_t)&((struct device *)0)->x) 158 159static switches_t dswitches[]= { 160 { "FILE", OFFS(name), T_STRING }, 161 { "OFFSET", OFFS(offset), T_UINT }, 162 { "PARTITION", OFFS(partition), T_UINT }, 163 { "FAT", OFFS(fat_bits), T_INT }, 164 { "FAT_BITS", OFFS(fat_bits), T_UINT }, 165 { "MODE", OFFS(mode), T_UINT }, 166 { "TRACKS", OFFS(tracks), T_UINT }, 167 { "CYLINDERS", OFFS(tracks), T_UINT }, 168 { "HEADS", OFFS(heads), T_UINT }, 169 { "SECTORS", OFFS(sectors), T_UINT }, 170 { "HIDDEN", OFFS(hidden), T_UINT }, 171 { "PRECMD", OFFS(precmd), T_STRING }, 172 { "BLOCKSIZE", OFFS(blocksize), T_UINT }, 173 { "CODEPAGE", OFFS(codepage), T_UINT } 174}; 175 176static void maintain_default_drive(char drive) 177{ 178 if(default_drive == ':') 179 return; /* we have an image */ 180 if(default_drive == '\0' || 181 default_drive > drive) 182 default_drive = drive; 183} 184 185char get_default_drive(void) 186{ 187 if(default_drive != '\0') 188 return default_drive; 189 else 190 return 'A'; 191} 192 193static void syntax(const char *msg, int thisLine) 194{ 195 char drive='\0'; 196 if(thisLine) 197 lastTokenLinenumber = linenumber; 198 if(cur_dev >= 0) 199 drive = devices[cur_dev].drive; 200 fprintf(stderr,"Syntax error at line %d ", lastTokenLinenumber); 201 if(drive) fprintf(stderr, "for drive %c: ", drive); 202 if(token) fprintf(stderr, "column %ld ", (long)(token - buffer)); 203 fprintf(stderr, "in file %s: %s\n", filename, msg); 204 exit(1); 205} 206 207static void get_env_conf(void) 208{ 209 char *s; 210 unsigned int i; 211 212 for(i=0; i< sizeof(global_switches) / sizeof(*global_switches); i++) { 213 s = getenv(global_switches[i].name); 214 if(s) { 215 if(global_switches[i].type == T_INT) 216 * ((int *)global_switches[i].address) = (int) strtol(s,0,0); 217 if(global_switches[i].type == T_UINT) 218 * ((int *)global_switches[i].address) = (unsigned int) strtoul(s,0,0); 219 else if (global_switches[i].type == T_STRING) 220 * ((char **)global_switches[i].address) = s; 221 } 222 } 223} 224 225static int mtools_getline(void) 226{ 227 if(!fp || !fgets(buffer, MAX_LINE_LEN, fp)) 228 return -1; 229 linenumber++; 230 pos = buffer; 231 token_nr = 0; 232 buffer[MAX_LINE_LEN] = '\0'; 233 if(strlen(buffer) == MAX_LINE_LEN) 234 syntax("line too long", 1); 235 return 0; 236} 237 238static void skip_junk(int expect) 239{ 240 lastTokenLinenumber = linenumber; 241 while(!pos || !*pos || strchr(" #\n\t", *pos)) { 242 if(!pos || !*pos || *pos == '#') { 243 if(mtools_getline()) { 244 pos = 0; 245 if(expect) 246 syntax("end of file unexpected", 1); 247 return; 248 } 249 } else 250 pos++; 251 } 252 token_nr++; 253} 254 255/* get the next token */ 256static char *get_next_token(void) 257{ 258 skip_junk(0); 259 if(!pos) { 260 token_length = 0; 261 token = 0; 262 return 0; 263 } 264 token = pos; 265 token_length = strcspn(token, " \t\n#:="); 266 pos += token_length; 267 return token; 268} 269 270static int match_token(const char *template) 271{ 272 return (strlen(template) == token_length && 273 !strncasecmp(template, token, token_length)); 274} 275 276static void expect_char(char c) 277{ 278 char buf[11]; 279 280 skip_junk(1); 281 if(*pos != c) { 282 sprintf(buf, "expected %c", c); 283 syntax(buf, 1); 284 } 285 pos++; 286} 287 288static char *get_string(void) 289{ 290 char *end, *str; 291 292 skip_junk(1); 293 if(*pos != '"') 294 syntax(" \" expected", 0); 295 str = pos+1; 296 end = strchr(str, '\"'); 297 if(!end) 298 syntax("unterminated string constant", 1); 299 *end = '\0'; 300 pos = end+1; 301 return str; 302} 303 304static unsigned int get_unumber(void) 305{ 306 char *last; 307 unsigned int n; 308 309 skip_junk(1); 310 last = pos; 311 n=(unsigned int) strtoul(pos, &pos, 0); 312 if(last == pos) 313 syntax("numeral expected", 0); 314 pos++; 315 token_nr++; 316 return n; 317} 318 319static unsigned int get_number(void) 320{ 321 char *last; 322 int n; 323 324 skip_junk(1); 325 last = pos; 326 n=(int) strtol(pos, &pos, 0); 327 if(last == pos) 328 syntax("numeral expected", 0); 329 pos++; 330 token_nr++; 331 return n; 332} 333 334/* purge all entries pertaining to a given drive from the table */ 335static void purge(char drive, int fn) 336{ 337 int i,j; 338 339 drive = toupper(drive); 340 for(j=0, i=0; i < cur_devs; i++) { 341 if(devices[i].drive != drive || 342 devices[i].file_nr == fn) 343 devices[j++] = devices[i]; 344 } 345 cur_devs = j; 346} 347 348static void grow(void) 349{ 350 if(cur_devs >= nr_dev - 2) { 351 nr_dev = (cur_devs + 2) << 1; 352 if(!(devices=Grow(devices, nr_dev, struct device))){ 353 printOom(); 354 exit(1); 355 } 356 } 357} 358 359 360static void init_drive(void) 361{ 362 memset((char *)&devices[cur_dev], 0, sizeof(struct device)); 363 devices[cur_dev].ssize = 2; 364} 365 366/* prepends a device to the table */ 367static void prepend(void) 368{ 369 int i; 370 371 grow(); 372 for(i=cur_devs; i>0; i--) 373 devices[i] = devices[i-1]; 374 cur_dev = 0; 375 cur_devs++; 376 init_drive(); 377} 378 379 380/* appends a device to the table */ 381static void append(void) 382{ 383 grow(); 384 cur_dev = cur_devs; 385 cur_devs++; 386 init_drive(); 387} 388 389 390static void finish_drive_clause(void) 391{ 392 char drive; 393 if(cur_dev == -1) { 394 trusted = 0; 395 return; 396 } 397 drive = devices[cur_dev].drive; 398 if(!devices[cur_dev].name) 399 syntax("missing filename", 0); 400 if(devices[cur_dev].tracks || 401 devices[cur_dev].heads || 402 devices[cur_dev].sectors) { 403 if(!devices[cur_dev].tracks || 404 !devices[cur_dev].heads || 405 !devices[cur_dev].sectors) 406 syntax("incomplete geometry: either indicate all of track/heads/sectors or none of them", 0); 407 if(!(devices[cur_dev].misc_flags & 408 (MFORMAT_ONLY_FLAG | FILTER_FLAG))) 409 syntax("if you supply a geometry, you also must supply one of the `mformat_only' or `filter' flags", 0); 410 } 411 devices[cur_dev].file_nr = file_nr; 412 devices[cur_dev].cfg_filename = filename; 413 if(! (flag_mask & PRIV_FLAG) && IS_SCSI(&devices[cur_dev])) 414 devices[cur_dev].misc_flags |= PRIV_FLAG; 415 if(!trusted && (devices[cur_dev].misc_flags & PRIV_FLAG)) { 416 fprintf(stderr, 417 "Warning: privileged flag ignored for drive %c: defined in file %s\n", 418 toupper(devices[cur_dev].drive), filename); 419 devices[cur_dev].misc_flags &= ~PRIV_FLAG; 420 } 421 trusted = 0; 422 cur_dev = -1; 423} 424 425static int set_var(struct switches_l *switches, int nr, 426 caddr_t base_address) 427{ 428 int i; 429 for(i=0; i < nr; i++) { 430 if(match_token(switches[i].name)) { 431 expect_char('='); 432 if(switches[i].type == T_UINT) 433 * ((int *)((long)switches[i].address+base_address)) = 434 get_unumber(); 435 if(switches[i].type == T_INT) 436 * ((int *)((long)switches[i].address+base_address)) = 437 get_number(); 438 else if (switches[i].type == T_STRING) 439 * ((char**)((long)switches[i].address+base_address))= 440 strdup(get_string()); 441 return 0; 442 } 443 } 444 return 1; 445} 446 447static int set_openflags(struct device *dev) 448{ 449 unsigned int i; 450 451 for(i=0; i < sizeof(openflags) / sizeof(*openflags); i++) { 452 if(match_token(openflags[i].name)) { 453 dev->mode |= openflags[i].flag; 454 return 0; 455 } 456 } 457 return 1; 458} 459 460static int set_misc_flags(struct device *dev) 461{ 462 unsigned int i; 463 464 for(i=0; i < sizeof(misc_flags) / sizeof(*misc_flags); i++) { 465 if(match_token(misc_flags[i].name)) { 466 flag_mask |= misc_flags[i].flag; 467 skip_junk(0); 468 if(pos && *pos == '=') { 469 pos++; 470 switch(get_number()) { 471 case 0: 472 return 0; 473 case 1: 474 break; 475 default: 476 syntax("expected 0 or 1", 0); 477 } 478 } 479 dev->misc_flags |= misc_flags[i].flag; 480 return 0; 481 } 482 } 483 return 1; 484} 485 486static int set_def_format(struct device *dev) 487{ 488 unsigned int i; 489 490 for(i=0; i < sizeof(default_formats)/sizeof(*default_formats); i++) { 491 if(match_token(default_formats[i].name)) { 492 if(!dev->ssize) 493 dev->ssize = 2; 494 if(!dev->tracks) 495 dev->tracks = default_formats[i].tracks; 496 if(!dev->heads) 497 dev->heads = default_formats[i].heads; 498 if(!dev->sectors) 499 dev->sectors = default_formats[i].sectors; 500 if(!dev->fat_bits) 501 dev->fat_bits = default_formats[i].fat_bits; 502 return 0; 503 } 504 } 505 return 1; 506} 507 508static int parse_one(int privilege); 509 510/* check for offset embedded in file name, in the form file@@offset[SKMG] */ 511static off_t get_offset(char *name) { 512 char s, *ofsp, *endp = NULL; 513 off_t ofs; 514 515 ofsp = strstr(devices[cur_dev].name, "@@"); 516 if (ofsp == NULL) 517 return 0; /* no separator */ 518 ofs = strtol(ofsp+2, &endp, 0); 519 if (ofs <= 0) 520 return 0; /* invalid or missing offset */ 521 s = *endp++; 522 if (s) { /* trailing char, see if it is a size specifier */ 523 endp++; 524 if (s == 's' || s == 'S') /* sector */ 525 ofs <<= 9; 526 else if (s == 'k' || s == 'K') /* kb */ 527 ofs <<= 10; 528 else if (s == 'm' || s == 'M') /* Mb */ 529 ofs <<= 20; 530 else if (s == 'g' || s == 'G') /* Gb */ 531 ofs <<= 30; 532 else 533 return 0; /* invalid character */ 534 if (*endp) 535 return 0; /* extra char, invalid */ 536 } 537 *ofsp = '\0'; /* truncate file name */ 538 return ofs; 539} 540 541void set_cmd_line_image(char *img, int flags) { 542 char *name; 543 prepend(); 544 devices[cur_dev].drive = ':'; 545 default_drive = ':'; 546 devices[cur_dev].name = name = strdup(img); 547 devices[cur_dev].fat_bits = 0; 548 devices[cur_dev].tracks = 0; 549 devices[cur_dev].heads = 0; 550 devices[cur_dev].sectors = 0; 551 devices[cur_dev].offset = get_offset(name); 552 if (strchr(devices[cur_dev].name, '|')) { 553 char *pipechar = strchr(devices[cur_dev].name, '|'); 554 *pipechar = 0; 555 strncpy(buffer, pipechar+1, MAX_LINE_LEN); 556 buffer[MAX_LINE_LEN] = '\0'; 557 fp = NULL; 558 filename = "{command line}"; 559 linenumber = 0; 560 lastTokenLinenumber = 0; 561 pos = buffer; 562 token = 0; 563 while (parse_one(0)); 564 } 565} 566 567static void parse_old_device_line(char drive) 568{ 569 char name[MAXPATHLEN]; 570 int items; 571 long offset; 572 573 /* finish any old drive */ 574 finish_drive_clause(); 575 576 /* purge out data of old configuration files */ 577 purge(drive, file_nr); 578 579 /* reserve slot */ 580 append(); 581 items = sscanf(token,"%c %s %i %i %i %i %li", 582 &devices[cur_dev].drive,name,&devices[cur_dev].fat_bits, 583 &devices[cur_dev].tracks,&devices[cur_dev].heads, 584 &devices[cur_dev].sectors, &offset); 585 devices[cur_dev].offset = (off_t) offset; 586 switch(items){ 587 case 2: 588 devices[cur_dev].fat_bits = 0; 589 /* fall thru */ 590 case 3: 591 devices[cur_dev].sectors = 0; 592 devices[cur_dev].heads = 0; 593 devices[cur_dev].tracks = 0; 594 /* fall thru */ 595 case 6: 596 devices[cur_dev].offset = 0; 597 /* fall thru */ 598 default: 599 break; 600 case 0: 601 case 1: 602 case 4: 603 case 5: 604 syntax("bad number of parameters", 1); 605 exit(1); 606 } 607 if(!devices[cur_dev].tracks){ 608 devices[cur_dev].sectors = 0; 609 devices[cur_dev].heads = 0; 610 } 611 612 devices[cur_dev].drive = toupper(devices[cur_dev].drive); 613 maintain_default_drive(devices[cur_dev].drive); 614 if (!(devices[cur_dev].name = strdup(name))) { 615 printOom(); 616 exit(1); 617 } 618 finish_drive_clause(); 619 pos=0; 620} 621 622static int parse_one(int privilege) 623{ 624 int action=0; 625 626 get_next_token(); 627 if(!token) 628 return 0; 629 630 if((match_token("drive") && ((action = 1)))|| 631 (match_token("drive+") && ((action = 2))) || 632 (match_token("+drive") && ((action = 3))) || 633 (match_token("clear_drive") && ((action = 4))) ) { 634 /* finish off the previous drive */ 635 finish_drive_clause(); 636 637 get_next_token(); 638 if(token_length != 1) 639 syntax("drive letter expected", 0); 640 641 if(action==1 || action==4) 642 /* replace existing drive */ 643 purge(token[0], file_nr); 644 if(action==4) 645 return 1; 646 if(action==3) 647 prepend(); 648 else 649 append(); 650 memset((char*)(devices+cur_dev), 0, sizeof(*devices)); 651 trusted = privilege; 652 flag_mask = 0; 653 devices[cur_dev].drive = toupper(token[0]); 654 maintain_default_drive(devices[cur_dev].drive); 655 expect_char(':'); 656 return 1; 657 } 658 if(token_nr == 1 && token_length == 1) { 659 parse_old_device_line(token[0]); 660 return 1; 661 } 662 663 if((cur_dev < 0 || 664 (set_var(dswitches, 665 sizeof(dswitches)/sizeof(*dswitches), 666 (caddr_t)&devices[cur_dev]) && 667 set_openflags(&devices[cur_dev]) && 668 set_misc_flags(&devices[cur_dev]) && 669 set_def_format(&devices[cur_dev]))) && 670 set_var(global_switches, 671 sizeof(global_switches)/sizeof(*global_switches), 0)) 672 syntax("unrecognized keyword", 1); 673 return 1; 674} 675 676static int parse(const char *name, int privilege) 677{ 678 if(fp) { 679 fprintf(stderr, "File descriptor already set (%p)!\n", fp); 680 exit(1); 681 } 682 fp = fopen(name, "r"); 683 if(!fp) 684 return 0; 685 file_nr++; 686 filename = name; /* no strdup needed: although lifetime of variable 687 exceeds this function (due to dev->cfg_filename), 688 we know that the name is always either 689 1. a constant 690 2. a statically allocate buffer 691 3. an environment variable that stays unchanged 692 */ 693 linenumber = 0; 694 lastTokenLinenumber = 0; 695 pos = 0; 696 token = 0; 697 cur_dev = -1; /* no current device */ 698 699 while(parse_one(privilege)); 700 finish_drive_clause(); 701 fclose(fp); 702 filename = NULL; 703 fp = NULL; 704 return 1; 705} 706 707void read_config(void) 708{ 709 char *homedir; 710 char *envConfFile; 711 static char conf_file[MAXPATHLEN+sizeof(CFG_FILE1)]; 712 713 714 /* copy compiled-in devices */ 715 file_nr = 0; 716 cur_devs = nr_const_devices; 717 nr_dev = nr_const_devices + 2; 718 devices = NewArray(nr_dev, struct device); 719 if(!devices) { 720 printOom(); 721 exit(1); 722 } 723 if(nr_const_devices) 724 memcpy(devices, const_devices, 725 nr_const_devices*sizeof(struct device)); 726 727 (void) ((parse(CONF_FILE,1) | 728 parse(LOCAL_CONF_FILE,1) | 729 parse(SYS_CONF_FILE,1)) || 730 (parse(OLD_CONF_FILE,1) | 731 parse(OLD_LOCAL_CONF_FILE,1))); 732 /* the old-name configuration files only get executed if none of the 733 * new-name config files were used */ 734 735 homedir = get_homedir(); 736 if ( homedir ){ 737 strncpy(conf_file, homedir, MAXPATHLEN ); 738 conf_file[MAXPATHLEN]='\0'; 739 strcat( conf_file, CFG_FILE1); 740 parse(conf_file,0); 741 } 742 memset((char *)&devices[cur_devs],0,sizeof(struct device)); 743 744 envConfFile = getenv("MTOOLSRC"); 745 if(envConfFile) 746 parse(envConfFile,0); 747 748 /* environmental variables */ 749 get_env_conf(); 750 if(mtools_skip_check) 751 mtools_fat_compatibility=1; 752} 753 754void mtoolstest(int argc, char **argv, int type) 755{ 756 /* testing purposes only */ 757 struct device *dev; 758 char drive='\0'; 759 760 if(argc > 1 && argv[1][0] && argv[1][1] == ':') { 761 drive = toupper(argv[1][0]); 762 } 763 764 for (dev=devices; dev->name; dev++) { 765 if(drive && drive != dev->drive) 766 continue; 767 printf("drive %c:\n", dev->drive); 768 printf("\t#fn=%d mode=%d ", 769 dev->file_nr, dev->mode); 770 if(dev->cfg_filename) 771 printf("defined in %s\n", dev->cfg_filename); 772 else 773 printf("builtin\n"); 774 printf("\tfile=\"%s\" fat_bits=%d \n", 775 dev->name,dev->fat_bits); 776 printf("\ttracks=%d heads=%d sectors=%d hidden=%d\n", 777 dev->tracks, dev->heads, dev->sectors, dev->hidden); 778 printf("\toffset=0x%lx\n", (long) dev->offset); 779 printf("\tpartition=%d\n", dev->partition); 780 781 if(dev->misc_flags) 782 printf("\t"); 783 784 if(DO_SWAP(dev)) 785 printf("swap "); 786 if(IS_SCSI(dev)) 787 printf("scsi "); 788 if(IS_PRIVILEGED(dev)) 789 printf("privileged"); 790 if(IS_MFORMAT_ONLY(dev)) 791 printf("mformat_only "); 792 if(SHOULD_USE_VOLD(dev)) 793 printf("vold "); 794#ifdef USE_XDF 795 if(SHOULD_USE_XDF(dev)) 796 printf("use_xdf "); 797#endif 798 if(dev->misc_flags) 799 printf("\n"); 800 801 if(dev->mode) 802 printf("\t"); 803#ifdef O_SYNC 804 if(dev->mode & O_SYNC) 805 printf("sync "); 806#endif 807#ifdef O_NDELAY 808 if((dev->mode & O_NDELAY)) 809 printf("nodelay "); 810#endif 811#ifdef O_EXCL 812 if((dev->mode & O_EXCL)) 813 printf("exclusive "); 814#endif 815 if(dev->mode) 816 printf("\n"); 817 818 if(dev->precmd) 819 printf("\tprecmd=%s\n", dev->precmd); 820 821 printf("\n"); 822 } 823 824 printf("mtools_fat_compatibility=%d\n",mtools_fat_compatibility); 825 printf("mtools_skip_check=%d\n",mtools_skip_check); 826 printf("mtools_lower_case=%d\n",mtools_ignore_short_case); 827 828 exit(0); 829} 830