/* MS: 0 ok, 3 user aborted, 4 fatal error, 5 not confirmed */ void Exit(int mserror, int fderror) { if ((mserror==3) || (mserror==4)) { /* aborted by user / fatal error? */ Lock_Unlock_Drive(0); /* remove inner lock */ Lock_Unlock_Drive(0); /* remove outer lock */ /* if there were less locks than unlocks, no problem: checked below. */ } #ifdef __FORMAT_DEBUG__ exit(fderror); /* use the "verbose errorlevel" */ #endif if (mserror==4) /* "fatal error"? */ printf(" [Code %d]\n", fderror); exit(mserror); /* use the "normal errorlevel" */ } /* Exit */
/* ///////////////////////////////////////////////////////////////////////////// // MAIN ROUTINE ///////////////////////////////////////////////////////////////////////////// */ void main(int argc, char *argv[]) { int ch; int index; int n; int found_format_sectors_per_track = 0; int found_format_heads = 0; #define MIRROR 1 #define UNFORMAT 2 #define WHAT_FORMAT 1 /* new 0.91p, for custom "... completed" messages */ #define WHAT_QUICK 2 #define WHAT_SAFE 3 #define WHAT_UN 4 #define WHAT_MIRROR 5 int what_completed = 0; /* new 0.91p */ int special = 0; /* mirror / unformat mode indicator */ int align = 1; /* new 0.91m */ int drive_letter_found = FALSE; Initialization(); param.n = FALSE; param.t = FALSE; param.v = FALSE; param.q = FALSE; param.u = FALSE; param.b = FALSE; param.s = FALSE; param.f = FALSE; param.one = FALSE; param.four = FALSE; param.eight = FALSE; debug_prog = FALSE; /* if FORMAT is typed without any options */ if (argc == 1) { Display_Help_Screen(0); /* SHORT version */ Exit(2,2); /* Exit(0,1); pre 0.91v */ } /* (jh) check command line */ while ( (index = getopt (argc, argv, "V:v:Z:z:QqUuBbSsYyAa148F:f:T:t:N:n:/")) != EOF) { switch(index) { case 'V': case 'v': param.v = TRUE; /* avoid overflow of param.volume_label (12 chars) */ /* need to skip over first character (':') */ strncpy (param.volume_label, optarg+1, 11); param.volume_label[11] = '\0'; for (n = 0; param.volume_label[n] != '\0'; n++) { ch = param.volume_label[n]; if (!isLabelChar (ch)) /* new valid-character check 0.91u */ { printf ("Illegal character in volume label: %c\n", ch); printf ("Allowed chars are 0-9, A-Z, space, special / country-specific chars 128-255\n" "and all of"); for (ch = ' '+1; ch < 128; ch++) { if ( (!isalnum (ch)) && (isLabelChar (ch)) ) printf (" %c", ch); } /* enumerate legal chars */ printf ("\nbut no control chars and none of"); for (ch = ' '+1; ch < 128; ch++) { if (!isLabelChar (ch)) printf (" %c", ch); } /* enumerate illegal chars */ printf ("\n"); Exit(0,2); } /* illegal char in label */ param.volume_label[n] = toupper (ch); } /* check label */ break; case 'Z': /* our getopt has no "long" support, so we use /Z:keyword */ case 'z': /* 0.91l - extra (long) options */ if (!stricmp(optarg+1,"mirror")) /* +1 to skip initial ":" */ { special = MIRROR; /* take a new mirror data snapshot */ break; } if (!stricmp(optarg+1,"unformat")) { special = UNFORMAT; /* revert to mirrored state, dangerous! */ break; } if (!stricmp(optarg+1,"seriously")) { param.force_yes = TRUE + TRUE; /* User MEANS to format harddisk */ break; } if (!stricmp(optarg+1,"longhelp")) { Display_Help_Screen(1); /* LONG version */ Exit(2,2); /* Exit(0,1); pre 0.91v */ } printf("Invalid /Z:mode - valid: MIRROR, UNFORMAT, SERIOUSLY\n"); Exit(2,2); /* Exit(0,2); pre 0.91v */ break; case 'Q': /* quick - flush metadata only */ case 'q': param.q = TRUE; break; case 'U': /* unconditional - full format */ case 'u': param.u = TRUE; break; case 'B': /* reserve space for system, deprecated */ case 'b': param.b = TRUE; break; case 'S': /* run SYS command */ case 's': param.s = TRUE; break; case 'Y': /* assume yes for confirmations - "undocumented" */ case 'y': if (param.force_yes == FALSE) param.force_yes = TRUE; break; case 'D': /* debug mode, more verbose output */ case 'd': debug_prog = TRUE; break; case 'A': /* 0.91m - align mode, force FAT size to 2k*n and */ case 'a': /* so on to force data clusters to start at 4k*n */ align = 8; break; case '1': /* one side only */ param.one = TRUE; break; case '4': /* 360k disk in 1.2M drive */ param.four = TRUE; break; case '8': /* 8 sectors per track */ param.eight = TRUE; break; case 'F': /* /F:size */ case 'f': /* /F:size */ param.f = TRUE; n = atoi (optarg + 1); if ((n == 160) || (n == 180) || (n == 320) || (n == 360) || (n == 720) || (n == 1200) || (n == 1440) || (n == 2880) || (n == 400) || (n == 800) || (n == 1680) || (n == 3360) || (n == 1494) || (n == 1743) || (n == 3486)) { param.size = n; } else { printf("Standard: 160, 180, 320, 360, 720, 1200, 1440, 2880.\n"); printf("Special: 400, 800, 1680, 3360, 1494, 1743, 3486.\n"); IllegalArg("/F",optarg); } break; case 'T': /* tracks (cylinders) */ case 't': /* tracks (cylinders) */ param.t = TRUE; n = atoi (optarg + 1); if ((n == 40) || (n == 80) || (n == 83)) { param.cylinders = n; } else { printf("Ok: 40, 80. ???: 83.\n"); IllegalArg("/T",optarg); } break; case 'N': /* sectors per track */ case 'n': /* sectors per track */ param.n = TRUE; n = atoi (optarg + 1); if ((n == 8) || (n == 9) || (n == 15) || (n == 18) || (n == 36) || (n == 10) || (n == 21) || (n == 42)) { param.sectors = n; } else { printf("Standard: 8, 9, 15, 18, 36.\n"); printf("Special: 10, 21, 42.\n"); IllegalArg("/N",optarg); } break; case '~': param.drive_letter[0] = toupper (optarg[0]); param.drive_number = param.drive_letter[0] - 'A'; param.drive_letter[1] = ':'; drive_letter_found = TRUE; break; case '?': Display_Help_Screen(0); /* SHORT version */ Exit(2,2); /* Exit(0,1); pre 0.91v */ case '/': /* Ignore '/' in middle of option */ break; default: printf("Unrecognized option: /%c\n", index); Display_Help_Screen(0); /* SHORT version */ Exit(2,2); /* Exit(4,2); pre 0.91v */ } /* switch (index) */ } /* for all args (getopt) */ if ( (argc > optind) && (isalpha (argv[optind][0])) && (argv[optind][1] == ':') && (argv[optind][2] == '\0') && (drive_letter_found == FALSE) ) { param.drive_letter[0] = toupper (argv[optind][0]); param.drive_number = param.drive_letter[0] - 'A'; param.drive_letter[1] = ':'; } else if (drive_letter_found == FALSE) { printf("Must specify drive letter.\n"); Exit(0,2); } /* (jh) done with parsing command line */ /* if FORMAT is typed with a drive letter */ if (debug_prog==TRUE) { printf("\n[DEBUG] FORMAT " VERSION ", selected drive %c:\n", param.drive_letter[0]); printf("[DEBUG] Sector buffer at %04x:%04x, track buffer at %04x:%04x\n", FP_SEG(sector_buffer), FP_OFF(sector_buffer), FP_SEG(huge_sector_buffer), FP_OFF(huge_sector_buffer) ); /* moved this message to this place in 0.91s */ } /* 0.91t - abort if subst, join, assign or remote drives */ Check_Remote_Subst(); /* only has to be invoked once */ /* Set the type of disk */ if (param.drive_number>1) param.drive_type=HARD; else param.drive_type=FLOPPY; /* *** TODO: complain about ANY size determination if type HARD *** */ /* Ensure that valid switch combinations were entered */ if ( (param.b==TRUE) && (param.s==TRUE) ) Display_Invalid_Combination(); /* cannot reserve space for SYS and actually SYS at the same time */ if ( (param.v==TRUE) && (param.eight==TRUE) ) Display_Invalid_Combination(); /* no label allowed if 160k / 320k */ if ( ( (param.one==TRUE) || (param.four==TRUE) /* 360k in 1.2M drive */ ) && ( (param.f==TRUE) || (param.t==TRUE) || (param.n==TRUE) ) ) Display_Invalid_Combination(); /* cannot combine size/track/sector override with 1-sided / 360k */ if ( ( (param.t==TRUE) && (param.n!=TRUE) ) || ( (param.n==TRUE) && (param.t!=TRUE) ) ) { printf("You cannot combine /T and /N.\n"); Display_Invalid_Combination(); /* you must give BOTH track and sector arguments if giving either */ } if ( (param.f==TRUE) && ( (param.t==TRUE) || (param.n==TRUE) ) ) Display_Invalid_Combination(); /* you can only give EITHER size OR track/sector arguments */ #if 0 if ( ( (param.one==TRUE) || (param.four==TRUE) ) && (param.eight==TRUE) ) Display_Invalid_Combination(); /* 360k / one-sided are both not 8 sectors per track (360k is 40x2x9) */ #endif /* we do allow /8 to reach 160k (with /1) and 320k (with /4) */ /* it is more the other way round: we do not want 8 sector/track > 320k! */ /* ... */ if (param.one==TRUE) /* one-sided: 160k and 180k only */ { param.sides = 1; param.cylinders = 40; /* *** this is actually handled in Set_Floppy_Media_Type mostly *** */ } if (param.four==TRUE) /* 360k in 1200k drive */ { param.cylinders = 40; param.sectors = 9; } if (param.eight==TRUE) /* DOS 1.0 formats: 160k and 320k only */ { param.sectors = 8; } Lock_Unlock_Drive(1); /* lock drive (needed in Win9x / DOS 7.x) */ /* FIRST lock, file system still enabled. */ next_disk: /* User interaction. */ if (param.drive_type==FLOPPY && param.force_yes==FALSE) Ask_User_To_Insert_Disk(); if (!special) { if (param.drive_type==HARD && (param.force_yes!=(TRUE+TRUE)) ) Confirm_Hard_Drive_Formatting(1); /* 1 means "format" */ /* disabled harddisk "/Y" forced confirmation in 0.91b unless ZAPME -ea */ /* replaced by "/Z:seriously" in 0.91l -ea */ } else { if ((param.force_yes == FALSE) && (special == UNFORMAT)) Confirm_Hard_Drive_Formatting(0); /* 0 means "unformat" - 0.91l */ /* no confirmation requested for MIRROR ! */ } /* "optimized" a lot in 0.91h, hopefully not too much... -ea */ if ((!special) && (param.u==TRUE) && (param.q==FALSE)) /* full format / wipe? */ { param.existing_format = FALSE; /* do not even check if /U non-/Q mode */ } else { /* /Q /U clears metadata but does no format / wipe */ /* /Q -- clears metadata after backing it up */ /* -- -- works like /Q */ /* CONCLUSION: We do not really have to worry about current */ /* for harddisks in /Q /U mode (uses DOS kernel default BPB) */ if ( (param.drive_type!=FLOPPY) && (param.u==TRUE) ) { /* no check for harddisk /U mode nor for /U /Q mode there. */ param.existing_format = FALSE; } else /* it is a floppy --- or no /U flag given */ { if (debug_prog==TRUE) { if (special) { printf("[DEBUG] Checking for existing format\n"); } else { if (param.drive_type==FLOPPY) printf("[DEBUG] Checking whether we need low-level format.\n"); else printf("[DEBUG] Checking whether UNFORMAT data can be saved.\n"); } } /* debug_prog */ /* Check to see if the media is currently formatted. */ /* Needed for non-/U modes and for floppy disks, but in */ /* /U non-/Q mode we already assume "not formatted" anyway. */ param.existing_format = Check_For_Format(); /* trashes fat_type value! */ /* no problem, done before media type / harddisk parameter setup */ } /* check for existing format */ } /* not FORMAT /U mode */ Lock_Unlock_Drive(2); /* lock drive (needed in Win9x / DOS 7.x) */ /* SECOND lock, going lowlevel... - 0.91q */ /* Determine and set media parameters */ if (param.drive_type==FLOPPY) { /* <FLOPPY> */ if (param.existing_format==TRUE) { found_format_sectors_per_track = sector_buffer[0x18]; found_format_heads = sector_buffer[0x1a]; } Set_Floppy_Media_Type(); /* configure hardware, set up BPB, fat_type FAT12 */ #if DETECT_FLOPPY_TWICE if ((param.existing_format == FALSE) && (param.u==FALSE)) { /* try finding existing format again - after setting media type */ if (debug_prog==TRUE) printf("[DEBUG] Searching for existing format again...\n"); Lock_Unlock_Drive(0); /* re-enable filesystem access - 0.91q */ param.existing_format = Check_For_Format(); /* trashes fat_type! No prob. */ /* ... because it is done just before Set_Floppy_Media_Type */ Lock_Unlock_Drive(2); /* re-disable filesystem access - 0.91q */ Set_Floppy_Media_Type(); /* configure hardware AGAIN, set up BPB, fat_type FAT12 */ /* set media type again! - 0.91i */ if (param.existing_format==TRUE) { /* depends on Check_For_Format to fill sector_buffer... */ found_format_sectors_per_track = sector_buffer[0x18]; found_format_heads = sector_buffer[0x1a]; } } /* not /U and no existing format found */ #endif /* DETECT_FLOPPY_TWICE */ /* if already formatted floppy, check whether geometry will change */ if (param.existing_format == TRUE) { if ( ( parameter_block.bpb.sectors_per_cylinder != found_format_sectors_per_track ) || ( found_format_heads != parameter_block.bpb.number_of_heads ) ) { printf("Will change size by formatting - forcing full format\n"); printf("Old: %d sectors per track, %d heads. New: %d sect. %d heads\n", found_format_sectors_per_track, found_format_heads, parameter_block.bpb.sectors_per_cylinder, parameter_block.bpb.number_of_heads); param.existing_format = FALSE; } } /* existing format */ /* if no existing format, we force full format anyway */ } /* </FLOPPY> */ else /* if harddisk: */ { /* <HARDDISK> */ /* 0.91m: the only place where "align" is used is here, in BPB setup!!! */ Set_Hard_Drive_Media_Parameters(align); /* get default BPB etc., find FATxx fat_type */ Enable_Disk_Access(); } /* </HARDDISK> /* *** Maybe we should have done drive setup earlier, for Check_...? *** */ if (param.existing_format == FALSE) /* details changed in 0.91h */ { if ( (param.drive_type==FLOPPY) && ( (param.u!=TRUE) || (param.q!=FALSE) ) ) printf("Cannot find existing format - forcing full format\n"); if ( (param.drive_type!=FLOPPY) && (param.u==FALSE) ) printf("Cannot find existing format - not saving UNFORMAT data.\n"); } switch (special) /* 0.91l - special modes MIRROR and UNFORMAT */ { case 0: /* do nothing special */ break; case MIRROR: /* only update mirror data */ printf("Writing a copy of the system sectors to the end of the drive:\n"); printf("Boot sector, one FAT, root directory. Useful for UNFORMAT.\n"); #if 0 /* warning obsolete 0.91r */ // printf("WARNING: This will OVERWRITE up to %s of possibly used space!\n", // (param.fat_type==FAT32) ? "ca. 16 MB" : // ( (param.drive_type==FLOPPY) ? "ca. 14 kB" : "ca. 192 kB" ) ); #endif Save_File_System(0); /* includes filesystem usage stats display */ /* 0.91r: 0 is "abort if we would damage data" -ea */ what_completed = WHAT_MIRROR; /* new 0.91p */ goto format_complete; /* break; */ case UNFORMAT: /* only write back FAT/root/boot from mirror */ printf("Overwriting boot sector, FATs and root directory with\n"); printf("MIRROR/UNFORMAT data which you have saved earlier.\n"); Restore_File_System(); /* restore is an euphemism for what it does! */ what_completed = WHAT_UN; /* new 0.91p */ goto format_complete; /* break; */ default: printf("/Z:what???\n"); /* should never be reached */ } /* switch special */ /* ask for label if none given yet - 0.91r */ if ((!param.v) && (!param.force_yes)) { param.v = (Ask_Label(param.volume_label) != 0); /* printf("\n[%s]%s\n", param.volume_label, (param.v) ? "" : " (none)"); */ } /* ask for label */ /* Format Drive */ if ( ( (param.existing_format==FALSE) && (param.drive_type==FLOPPY) ) || ( (param.u==TRUE) && (param.q==FALSE) ) ) { /* /U is Unconditional Format. */ /* If floppy is unformatted or geometry changes, we must use this. */ printf(" Full Formatting (wiping all data)\n"); Unconditional_Format(); Create_File_System(); what_completed = WHAT_FORMAT; /* new 0.91p */ goto format_complete; } if ( ( (param.existing_format==FALSE) && (param.drive_type!=FLOPPY) ) || ( (param.u==TRUE) && (param.q==TRUE) ) ) /* changed in 0.91h */ { /* /Q /U is Quick Unconditional format */ /* Even unformatted harddisks do not need full "/U" format */ /* (harddisk /U format means "wipe all data, do surface scan) */ /* (harddisk LOWLEVEL format is never done by this program!) */ printf(" QuickFormatting (only flushing metadata)\n"); printf(" Warning: Resets bad cluster marks if any.\n"); Create_File_System(); what_completed = WHAT_QUICK; /* new 0.91p */ goto format_complete; } if ( (param.u==FALSE) /* && (param.q==TRUE) */ ) { /* this is the default, so if no /U given, it is irrelevant */ /* whether /Q is given or not... Should trigger FULL format */ /* if existing filesystem has other size or disk needs format */ /* /Q is Safe Quick Format (checking for existing bad cluster list) */ /* -- is Safe Quick Format (the same :-)) */ printf(" Safe QuickFormatting (trying to save UnFormat data)\n"); Save_File_System(1); /* side effect: checks for existing bad clust list */ /* 0.91r: 1 is "allow trashing of data clusters" -ea */ /* (unformat data is allowed to overwrite files) */ Create_File_System(); what_completed = WHAT_SAFE; /* new 0.91p */ goto format_complete; } format_complete: #if 0 // Force_Drive_Recheck(); /* must be delayed until drive unlocked - 0.91q */ #endif if ((bad_sector_map[0]>0) && (!special)) Record_Bad_Clusters(); /* write list of bad clusters */ /* may include copied list from previous filesystem - not in special modes! */ switch (what_completed) /* new 0.91p - custom message */ { case WHAT_FORMAT: printf("\nFormat"); break; case WHAT_QUICK: printf("\nQuickFormat"); break; case WHAT_SAFE: printf("\nSafe QuickFormat"); break; case WHAT_MIRROR: printf("\nMirror"); break; case WHAT_UN: printf("\nUnFormat"); break; } /* switch */ printf(" complete.\n"); Lock_Unlock_Drive(0); /* unlock drive again (in Win9x / DOS 7.x) */ /* release SECOND lock: enable filesystem */ Force_Drive_Recheck(); /* just in case... - 0.91q */ Lock_Unlock_Drive(0); /* unlock drive again (in Win9x / DOS 7.x) */ /* release FIRST lock: allow full access! */ if ((param.s==TRUE) && (!special)) { Write_System_Files(); /* call SYS */ Force_Drive_Recheck(); /* just in case... - 0.91q */ } if (!special) Display_Drive_Statistics(); if ((param.drive_type==FLOPPY) && (param.force_yes==FALSE)) { union REGS regs; /* printf("\nFormat another floppy (y/n)?\n"); */ if (!special) /* or use what_completed switch / case with WHAT_...? */ write(isatty(1) ? 1 : 2, "\nFormat", 7); else write(isatty(1) ? 1 : 2, "\nProcess", 8); write(isatty(1) ? 1 : 2, " another floppy (y/n)? ", 23); /* write to STDERR to keep message visible even if STDOUT redirected */ /* Get keypress */ regs.h.ah = 0x07; intdos(®s, ®s); if (toupper(regs.h.al) == 'Y') { int bads; printf("\n%s next floppy...\n", special ? "Processing" : "Formatting"); drive_statistics.bad_sectors = 0; bad_sector_map_pointer = 0; for (bads=0; bads < MAX_BAD_SECTORS; bads++) { bad_sector_map[bads] = 0; } Lock_Unlock_Drive(1); /* lock drive (needed in Win9x / DOS 7.x) */ /* FIRST lock, file system still enabled. */ /* (second level will be entered again when we really need it...) */ param.v = FALSE; /* force asking for new label for next disk */ goto next_disk; /* *** LOOP AROUND FOR MULTIPLE FLOPPY DISKS *** */ } printf("\n"); } /* another floppy question, possibly jumping back */ if (((debug_prog==TRUE) || (param.force_yes!=FALSE)) && (bad_sector_map[0]>0) && (!special)) exit(1); /* 0.91v for Alain: errorlevel 1 for "has bad clusters" */ /* (only if /z:seriously or /y or /d option active) */ exit(0); /* not using Exit() here: no dual errorlevel, no unlock */ }