/* >>> this is file LOADLINX.C compile it with TURBO-C in large model ============================================================================ LOADLIN v1.4 (C) 1994 Hans Lermen (lermen@elserv.ffm.fgan.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ---------------------------------------------------------------------------- Comments and bug reports are welcome and may be sent to: E-Mail: lermen@elserv.ffm.fgan.de SnailMail: Hans Lermen Am Muehlenweg 38 D53424 REMAGEN-Unkelbach GERMANY ============================================================================ This program has to be used as a "preprocessor" for LOADLIN, it does translation of DOSish drive numbering (C:,D:...) to Linux device names (/dev/...). In addition to "root=X:" LOADLINX accepts the same parameters as LOADLIN and passes them to it. But translation is NOT supported for parameter files (@param). === ========= ====== It can even be executed via shell= in CONFIG.SYS, but must reside in the same directory, where LOADLIN is located. Jacques Gelinas (jacques@solucorp.qc.ca) is the author of this algorithm ( I just implemented and enhanced it for LOADLIN and adapted it to the exact behavior of DOS ). It's use is intended for UMSDOS-users only (others do not have a DOS-partition for "root"). It is (at the time of LOADLIN-1.4) restricted to to systems with all drives AT-type or maximum 2 SCSI-drives over BIOS, mixed usage it not (yet) supported. USAGE: LOADLINX [--dv] loadlin_command_line --dv debug verbose, did't exec LOADLIN ============================================================================ */ #define DEBUGGING 0 #include #include #include #include #include #include #include #include #include enum { I13_reset_drive,I13_drive_status,I13_read_sector,I13_write_sector, I13_verify_sector,I13_format_track,I13_format_bad_track,I13_format_drive, I13_get_drive_parameters }; #define FCARRY 1 #define RETRY_MAX 5 typedef unsigned short word; typedef unsigned char byte; typedef unsigned long dword; typedef struct { byte bootable_flag; byte head_start; word tracksect_start; /* format as CX for INT13 */ byte partion_type; /* 0x01 = DOS, FAT12 0x04 = DOS, FAT16 (small DOS) 0x05 = extended partion 0x06 = DOS, FAT16 (big DOS) 0x83 = Linux native ... */ byte head_end; word tracksect_end; /* format as CX for INT13 */ dword start_sector; /* logical sector (counting from 0) of bootsect of this partion */ dword total_sectors; /* total number of sectors in this partition including bootsect */ } partition_typ; typedef struct { byte loader_code[0x1be]; partition_typ partitions[4]; word bootmagic; /* = 0xAA55 */ } MBR_typ; typedef struct { word cylinders; word heads; word sector_per_track; word phys_drive; MBR_typ mbr; char linux_dev; } my_drive_param_table_typ; #define MAX_DRIVES 2 /* I know, I know ... Two drives are enough, but I saw device drivers, which hook the INT13 and supplied 0x82, 0x83.. If we get report of someone using such drives, we will try a greater MAX_DRIVES*/ my_drive_param_table_typ drives[MAX_DRIVES]; int num_drives; MBR_typ embr; char devname[80]; int can_exit_to_dos=0; int verbose_only=0; void err_exit(char *tx) { #if DEBUGGING debug_write_close(); debug_read_close(); #endif if (tx) fprintf(stderr, "%s\n",tx); if (can_exit_to_dos) { if (tx) exit(1); else exit(0); } fprintf(stderr, "\n LOADLIN holding in idle loop, you must reboot\n"); while (1); } #if DEBUGGING /* >>>>>>>>>> debugging >>>>>>>>>> */ typedef struct { word kind; word phys_drive; dword sector; byte dummy[16-8]; union { MBR_typ mbr; struct REGPACK r; int cmos; } u; } debug_record_typ; int fdebug_out=-1; int fdebug_in=-1; debug_write_close() { if (fdebug_out>0) close(fdebug_out); fdebug_out=-1; } void debug_write_open(char *fname) { debug_write_close(); if ((fdebug_out=_creat(fname, FA_ARCH)) == -1 ) { perror(fname); err_exit("can't open output-debug file"); } } debug_read_close() { if (fdebug_in>0) close(fdebug_out); fdebug_in=-1; } void debug_read_open(char *fname) { debug_read_close(); if ((fdebug_in=_open(fname, O_RDONLY)) == -1 ) { perror(fname); err_exit("can't open input-debug file"); } } void debug_write(int kind, int physdrive, dword sector, void *buf) { debug_record_typ b; if (!fdebug_out) return; memcpy(&b.u.mbr,buf,sizeof(b.u.mbr)); b.kind=kind; b.phys_drive=physdrive; b.sector=sector; memset(&b.dummy,0,sizeof(b.dummy)); write(fdebug_out,&b,sizeof(b)); } int debug_read(int kind, int physdrive, dword sector, void *buf) { debug_record_typ b; int r; int size; if (!fdebug_in) return -1; lseek(fdebug_in,0,SEEK_SET); while (read(fdebug_in,&b,sizeof(b)) == sizeof(b)) { if ( (b.phys_drive==physdrive) && (b.kind==kind) && (b.sector==sector) ) { switch (kind) { case 1: size= sizeof(b.u.r);break; case 2: size= sizeof(b.u.cmos); break; default: size= sizeof(b.u.mbr); break; } memcpy(buf,&b.u.mbr,size); return 0; } } return -1; } int read_file(char *name, void *buf, int bufsize) { int f; if ((f=_open(name, O_RDONLY)) == -1 ) { printf("can't open input file %s\n",name); return -1; } bufsize=read(f,buf,bufsize); close(f); return bufsize; } void generate_virtual_drive_manually() { dword virtual_total_size=(64*1024L*2); char name[80]; int i,part_type,numdrives; dword ii,first_sector; struct REGPACK regs; int virtually = 0; int more=0; int physdrive; int has_bootable; printf("enter name of output file:\n"); gets(name); if (!name[0]) err_exit(""); debug_write_open(name); printf("how many drives total (1 or 2, default=1):\n"); gets(name); if (name[0] != '2') name[0] = 1; numdrives=name[0] & 3; more=numdrives; do { has_bootable=0; printf("enter drive to be simulated (0 or 1, empty=0):\n"); gets(name); name[0] &=1; physdrive= (name[0] & 1) +0x80; drives[0].phys_drive=physdrive; i=47; debug_write(2,drives[0].phys_drive,0, &i); regs.r_dx = numdrives; debug_write(1,drives[0].phys_drive,0, ®s); printf("enter name of file containing MBR (or empty if none ):\n"); gets(name); if (name[0]) { if (read_file(name, &drives[0].mbr, sizeof(drives[0].mbr))!=sizeof(drives[0].mbr)) err_exit(""); } else { virtually=1; memset(&drives[0].mbr,0,sizeof(drives[0].mbr)); drives[0].mbr.bootmagic = 0xAA55; first_sector=0x100; for (i=0; i<4; i++) { printf("enter type of partition %d:\n",i+1); gets(name); if (!name[0]) break; drives[0].mbr.partitions[i].partion_type = strtoul(name,0,0); if ((physdrive == 0x80) && (!has_bootable)) { printf("bootable partition ? (enter for NO, any other for YES)\n"); gets(name); if (name[0]) { drives[0].mbr.partitions[i].bootable_flag=0x80; has_bootable=1; } } drives[0].mbr.partitions[i].start_sector=first_sector; first_sector+=virtual_total_size; drives[0].mbr.partitions[i].total_sectors=virtual_total_size; } } debug_write(0,drives[0].phys_drive,0, &drives[0].mbr); for (i=0; i<4; i++) { if (drives[0].mbr.partitions[i].partion_type == 5) { printf("has extended partition\n"); first_sector=drives[0].mbr.partitions[i].start_sector; ii=first_sector; part_type=drives[0].mbr.partitions[i].partion_type; while (part_type == 5) { if (!virtually) { printf("needs bootrec for logical partition, sector = %ld\n" "enter name of file containing this bootrec\n",ii); gets(name); if (!name[0]) err_exit(""); if (read_file(name, &drives[0].mbr, sizeof(drives[0].mbr))!=sizeof(drives[0].mbr)) err_exit(""); printf("partition type is 0x%02x, enter new partition type ( empty for no change)\n", drives[0].mbr.partitions[0].partion_type); gets(name); if (name[0]) drives[0].mbr.partitions[0].partion_type=strtoul(name,0,0); } else { memset(&drives[0].mbr,0,sizeof(drives[0].mbr)); drives[0].mbr.bootmagic = 0xAA55; printf("enter type of sub-partition %d (default is 6):\n"); gets(name); if (!name[0]) drives[0].mbr.partitions[0].partion_type =6; else drives[0].mbr.partitions[0].partion_type = strtoul(name,0,0); drives[0].mbr.partitions[0].start_sector=ii-first_sector; drives[0].mbr.partitions[0].total_sectors=virtual_total_size; printf("is this the last sub-partition (enter for NO, any other for YES)\n"); gets(name); if (!name[0]) { drives[0].mbr.partitions[1].partion_type = 5; drives[0].mbr.partitions[1].start_sector=ii-first_sector+virtual_total_size; drives[0].mbr.partitions[1].total_sectors=virtual_total_size; } } debug_write(0,drives[0].phys_drive,ii, &drives[0].mbr); ii= first_sector+drives[0].mbr.partitions[1].start_sector; part_type=drives[0].mbr.partitions[1].partion_type; } break; } } more--; if (more>0) { printf("will you generate a second virtual drive (enter for NO, any other for YES)\n"); gets(name); if (!name[0]) more=-1; } } while (more>0); debug_write_close(); } #endif /* <<<<<<<<<< debugging <<<<<<<<<< */ int get_drive_parameters(int physdrive, my_drive_param_table_typ *t) { struct REGPACK r; int ret; t->phys_drive = physdrive; #if DEBUGGING if (!(ret=debug_read(1, physdrive, 0, &r))) return r.r_dx & 255; #endif r.r_ax=(I13_get_drive_parameters) << 8; r.r_dx=physdrive; intr(0x13,&r); if ((r.r_flags & FCARRY) || (r.r_ax & 0xff00)) return 0; t->cylinders = (r.r_cx >> 8) | ((r.r_cx << 2) & 0x300); t->cylinders++; t->heads = r.r_dx >> 8; t->heads++; t->sector_per_track = r.r_cx & 0x3f; ret = r.r_dx & 255; /* number of available drives */ #if DEBUGGING debug_write(1, physdrive, 0, &r); #endif return ret; } int phys_disk_read(int physdrive, int track, int head, int sector, void *buf) { /* NOTE: because we only access harddrives, which don't use DMA we never get DMA overrun, so "buf" can be unaligned. (SCSI-bioses, which hook into INT13 and use DMA, take care of DMA-overun themself). */ int i,s; for (i=0; iphys_drive, logsector, buf))) return r; #endif s= (dword)dp->heads * dp->sector_per_track; t = logsector / s; s = logsector % s; h = s / dp->sector_per_track; s = (s % dp->sector_per_track) +1 ; r=phys_disk_read(dp->phys_drive,t,h,s,buf); #if DEBUGGING debug_write(0, dp->phys_drive, logsector, buf); #endif return r; } unsigned char get_cmos_byte(int index) { enum {CMOS_INDEX=0x70,CMOS_DATA}; outportb(CMOS_INDEX,index); return inportb(CMOS_DATA); } int CMOS_harddisk_type(int drive) { enum {CMOS_HARDDISKS=0x12,CMOS_HARDDISK0=0x19,CMOS_HARDDISK1}; int h,r; #if DEBUGGING if (!(r=debug_read(2, 0x80+drive, 0, &h))) return h; #endif h = get_cmos_byte(CMOS_HARDDISKS); if (drive) { h &=15; drive=1; } else h >>=4; if (h<15) return h; r = get_cmos_byte(CMOS_HARDDISK0+drive); #if DEBUGGING debug_write(2, 0x80+drive, 0, &r); #endif return r; } int first_check() { int i; /* first we check, how much INT13 accessable drives we have We do this by getting the drive params for drive 0 (0x80) and looking at "consecutive number of drives". If we access a not existing drive, we may wait for LONG while until the BIOS returns. Impatient users like to apply the 3-finger-salut before this. ( oh dear... ) */ num_drives=get_drive_parameters(0x80, &drives[0]); if (verbose_only) printf("number of INT13 accessable drives: %d\n",num_drives); if (!num_drives) err_exit("can't access any hard drives"); if (num_drives > MAX_DRIVES) err_exit("..ouch.., to many drives, send report to lermen@elserv.ffm.fgan.de"); /* Ok, we have one drive at minimum, we get the MBR of it */ if (logical_disk_read(&drives[0],0,&drives[0].mbr)) err_exit("can't access MBR of drive 0"); /* now we do the same for all other drives */ for (i=1; i1 can't be AT. */ for (i=0; i1) drives[i].linux_dev='s'; else { if (CMOS_harddisk_type(i)) drives[i].linux_dev='h'; else drives[i].linux_dev='s'; } } } int get_bootpartition_of(int drive) { int i; for (i=0; i<4; i++) { switch (drives[drive].mbr.partitions[i].partion_type) { case 1: case 4: case 6: if (drives[drive].mbr.partitions[i].bootable_flag) { drives[drive].mbr.partitions[i].partion_type=0; /* avoid reusage */ return i+1; } } } return -1; } int get_primary_partition_of(int drive) { int i; for (i=0; i<4; i++) { switch (drives[drive].mbr.partitions[i].partion_type) { case 1: case 4: case 6: drives[drive].mbr.partitions[i].partion_type=0; /* avoid reusage */ return i+1; } } return -1; } char *decode_partition_type(int ptype){ switch (ptype) { case 0x00: return "Empty"; case 0x01: return "DOS 12-bit FAT"; case 0x02: return "XENIX root"; case 0x03: return "XENIX usr"; case 0x04: return "DOS 16-bit <32M"; case 0x05: return "Extended"; case 0x06: return "DOS 16-bit >=32"; case 0x07: return "OS/2 HPFS"; case 0x08: return "AIX"; case 0x09: return "AIX bootable"; case 0x0a: return "OPUS"; case 0x40: return "Venix 80286"; case 0x51: return "Novell?"; case 0x52: return "Microport"; case 0x63: return "GNU HURD"; case 0x64: return "Novell"; case 0x75: return "PC/IX"; case 0x80: return "Old MINIX"; case 0x81: return "Linux/MINIX"; case 0x82: return "Linux swap"; case 0x83: return "Linux native"; case 0x93: return "Amoeba"; case 0x94: return "Amoeba BBT"; case 0xb7: return "BSDI fs"; case 0xb8: return "BSDI swap"; case 0xc7: return "Syrinx"; case 0xdb: return "CP/M"; case 0xe1: return "DOS access"; case 0xe3: return "DOS R/O"; case 0xf2: return "DOS secondary"; case 0xff: return "BBT"; default: return "unknown"; } } print_extended_partitions_of(int drive, int part) { int ii; unsigned long sect0,sect; sect0=drives[drive].mbr.partitions[part].start_sector; sect=sect0; for (ii=1; ii<16 ;ii++) { if (logical_disk_read(&drives[drive],sect, &embr)) { fprintf(stderr,"drive %d extended partition %d sub-partition %d\n",drive,part,ii); err_exit("can't read partition header"); } if (embr.bootmagic != 0xAA55) return; printf( " log-part. %d %ld Mb %s\n", ii+4, embr.partitions[0].total_sectors / (2*1024), decode_partition_type(embr.partitions[0].partion_type) ); if ( (embr.partitions[1].partion_type != 5) || !embr.partitions[1].total_sectors ) return; sect= sect0+embr.partitions[1].start_sector; } } print_partitions_of(int drive) { int i,ptype; char *bootable; printf("drive %d == Linux device: /dev/%cd%c\n", drive,drives[drive].linux_dev,'a'+drive); for (i=0; i<4; i++) { ptype=drives[drive].mbr.partitions[i].partion_type; if (!ptype) return; if (drives[drive].mbr.partitions[i].bootable_flag) bootable=" bootable"; else bootable=""; printf( " partition %d %ld Mb %s%s\n", i+1, drives[drive].mbr.partitions[i].total_sectors / (2*1024), decode_partition_type(ptype), bootable ); if (ptype == 5) print_extended_partitions_of(drive, i); } } int get_extended_partitions_of(int drive, char dosdrive, char *currentdrive) { int i,ii; partition_typ *p; unsigned long sect0,sect; for (i=0; i<4; i++) { if (drives[drive].mbr.partitions[i].partion_type == 5) { /* found an extended partition, and walk through the chain of partition header only to count. Comment from Linus Thorvalds (linux/drivers/block/genhd.c): >>> * The logical partitions form a linked list, with each entry being * a partition table with two entries. The first entry * is the real data partition (with a start relative to the partition * table start). The second is a pointer to the next logical partition * (with a start relative to the entire extended partition). <<< end comment. */ drives[drive].mbr.partitions[i].partion_type=0; /* avoid reusage */ sect0=drives[drive].mbr.partitions[i].start_sector; sect=sect0; for (ii=1; ii<16 ;ii++) { if (logical_disk_read(&drives[drive],sect, &embr)) { fprintf(stderr,"drive %d extended partition %d sub-partition %d\n",drive,i,ii); err_exit("can't read partition header"); } if (embr.bootmagic != 0xAA55) return ii-1; switch (embr.partitions[0].partion_type) { case 1: case 4: case 6: { if (*currentdrive == dosdrive) return ii; (*currentdrive)++; break; } } if ( (embr.partitions[1].partion_type != 5) || !embr.partitions[1].total_sectors ) return -1; sect= sect0+embr.partitions[1].start_sector; } } } return -1; } assemble_devname_(int drive, int part, char *linuxdev) { strcpy(linuxdev,"root=/dev/"); linuxdev += 10; *linuxdev++ = drives[drive].linux_dev; *linuxdev++ = 'd'; *linuxdev++ = 'a'+drive; itoa(part,linuxdev,10); } int assemble_devname(int drive, int part, char dosdrive, char *d, char *linuxdev) { if (part<=0) return -1; if (dosdrive == *d) { assemble_devname_(drive,part,linuxdev); return 0; } (*d)++; return 1; } char *search_drive(char dosdrive, char *linuxdev) { char d; int part,i; dosdrive= tolower(dosdrive); if (verbose_only) for (i=0; i0) { if (dosdrive == d) { assemble_devname_(0,4+part,linuxdev); return linuxdev; } } /* now the rest of drive 0 */ do { part=get_primary_partition_of(0); if (!assemble_devname(0,part,dosdrive,&d,linuxdev)) return linuxdev; } while (part>0); /* now we go to drive 1, assigning all we have, but first the extended partions */ part=get_extended_partitions_of(1,dosdrive,&d); if (part>0) { if (dosdrive == d) { assemble_devname_(1,4+part,linuxdev); return linuxdev; } } /* now the rest of drive 1 */ do { part=get_primary_partition_of(1); if (!assemble_devname(1,part,dosdrive,&d,linuxdev)) return linuxdev; } while (part>0); /* we come here if dosdrive couldn't be translated */ return 0; } int parse_for_arg(char *search, int n,char **argv) { int i=0; while (*argv) { if (n) { if (!strnicmp(*argv++,search,n)) return i; } else { if (!stricmp(*argv++,search)) return i; } i++; } return 0; } void delete_arg(int arg, int *argc, char **argv) { memcpy(argv+arg,argv+arg+1,(*argc+1-arg)*4); (*argc)--; } typedef struct { unsigned short jmp_op; /* jmp short start_of_setup */ unsigned long setup_header_sign; unsigned short setup_header_version; void * setup_realmode_switch; unsigned short start_sys_seg; unsigned short kernel_version; } setup_header; #define SIGNATURE 0x53726448 /* setup header signature */ char * get_kernel_version_string(char *fname,char *buf,int sizebuf) { int f; setup_header h; if ((f=_open(fname, O_RDONLY)) == -1 ) { perror(fname); err_exit("can't open image file"); } lseek(f,0x200,SEEK_SET); if (read(f,&h,sizeof(h)) != sizeof(h)) { _close(f); err_exit("image has wrong format"); } if ((h.setup_header_sign == SIGNATURE) && (h.setup_header_version >= 0x105)) { lseek(f,0x200+h.kernel_version,SEEK_SET); if (read(f,buf,sizebuf) == sizebuf) return buf; } _close(f); return 0; } long int value_of(char **s) { int i=0; char s_[30]; while (isdigit(**s)) s_[i++]=*(*s)++; s_[i]=0; *(*s)++; /* this code HAS effect, though TURBO-C states that not */ return atoi(s_); } unsigned long version_string_to_binary(char *vstring) { unsigned long v=0; char *s; v =value_of(&vstring); v <<=8; v +=value_of(&vstring); v <<=8; v +=value_of(&vstring); v <<=8; if (vstring=strchr(vstring-1,'#')) { vstring++; v +=value_of(&vstring); } /* printf(">%08lx<\n",v); */ return v; } void check_kernel_version(char *imagename, char *version) { unsigned long v_desired; unsigned long v_actual; int check_below=0, ok; char buf[128]; if (get_kernel_version_string(imagename,buf,sizeof(buf))) { if (version[0]=='-') { check_below=1; version++; } v_desired=version_string_to_binary(version); v_actual=version_string_to_binary(buf); if (!(v_desired & 0xff)) v_actual &= ~0xFF; if (check_below) ok= v_actual <= v_desired; else ok= v_actual >= v_desired; if (!ok) err_exit("wrong kernel version"); } else err_exit("setup of image file has no version stamp, can\'t verify"); } void print_version_string(char *image) { char buf[128]; if (get_kernel_version_string(image,buf,sizeof(buf))) { fprintf(stderr, "\n%s %s\n",image,buf); err_exit(0); } else err_exit("setup of image file has no version stamp"); } /* -------------- response-file stuff -------------- */ FILE * fparam_in=0; param_read_close() { if (fparam_in) fclose(fparam_in); fparam_in=0; } void param_read_open(char *fname) { param_read_close(); if ((fparam_in=fopen(fname, "rt")) == 0 ) { perror(fname); err_exit("can't open params file"); } } #define MAX_OUR_ARGS 50 char *our_argv[MAX_OUR_ARGS+1]={0}; int our_argc=0; typedef struct { unsigned short opint20; unsigned short memend_frame; unsigned char dos_reserved4; unsigned char cpm_function_entry[0xa-0x5]; unsigned long int22_copy; unsigned long int23_copy; unsigned long int24_copy; unsigned short PID; unsigned char file_handles[20]; unsigned short envir_frame; unsigned long system_stack; unsigned short max_open_files; unsigned long file_handles_ptr; unsigned char dos_reserved38[0x50-0x38]; unsigned char high_language_dos_call[0x53-0x50]; unsigned char dos_reserved53[0x5c-0x53]; unsigned char FCB1[0x6c-0x5c]; unsigned char FCB2[0x80-0x6c]; unsigned char DTA[0x100-0x80]; } dos_segprefix; static build_param_buf(char **argv) { dos_segprefix *psp=MK_FP(_psp,0); unsigned segp; int ret; unsigned char *param_buf=(void *)0x90200000; ret=allocmem(0x1000,&segp); freemem(segp); if (ret==-1 && segp<0x9000) { if (psp->memend_frame >0x9c00) { argv++; /* skip argv[0] ( the program name ) */ while (*argv) param_buf+=sprintf(param_buf,"%s\n",*argv++); *param_buf=0; } else err_exit("not enough memory at top of 0x9C000, can\'t pass params to LOADLIN.EXE"); } else err_exit("not enough memory starting at 0x90000, can\'t pass params to LOADLIN.EXE"); } static put_our_arg(char *arg) { /* printf(">%s<\n",arg); */ if (our_argc1) _argv++; while (*_argv) { if (strcmp(*_argv,argv[i])) { if (c=strchr(*_argv,'=')) { j=parse_for_arg(*_argv,(c-*_argv)+1,our_argv); if (j) our_argv[j]=*_argv; else put_our_arg(*_argv); } else { if (!(parse_for_arg(*_argv,0,our_argv))) { if (!strcmp("ro",*_argv)) { if (j=(parse_for_arg("rw",0,our_argv))) our_argv[j]=*_argv; else put_our_arg(*_argv); } else { if (!strcmp("rw",*_argv)) { if (j=(parse_for_arg("ro",0,our_argv))) our_argv[j]=*_argv; else put_our_arg(*_argv); } else put_our_arg(*_argv); } } } } _argv++; } if (i>1) our_argv[1]=argv[1]; argv=(char **)&our_argv; argc=our_argc; } if ( (root=parse_for_arg("root=",5,argv)) && (argv[root][6] == ':') ) { int drivenum=tolower(argv[root][5]); if (drivenum >= 'c') { first_check(); second_check(); if (!search_drive(argv[root][5],devname)) err_exit("can't translate root device"); } else { if (drivenum == 'a') strcpy(devname,"root=/dev/fd0"); else strcpy(devname,"root=/dev/fd1"); } argv[root]=devname; } else { if (verbose_only) { first_check(); second_check(); search_drive('z',devname); /* err_exit(0); */ } } if (i=parse_for_arg("--version=",10,argv)) { check_kernel_version(argv[1],argv[i]+10); delete_arg(i, &argc, argv); } if (i=parse_for_arg("--version",0,argv)) { print_version_string(argv[1]); /* doesn't return */ } strcpy(lname,argv[0]); for (i=strlen(lname); lname[i] !='\\' ; i--); strcpy(lname+i+1,"LOADLIN.EXE"); if (verbose_only) { fprintf(stderr,"\n %s would be started with following command line:\n\n ",lname); argv++; while (*argv) fprintf(stderr," %s",*argv++); err_exit("\n"); } else { if (have_responsefile) { build_param_buf(argv); argv[1]="@@loadlinx@@"; argv[2]=0; } if (execv(lname,argv) == -1) err_exit("can't find/execute LOADLIN.EXE"); } }