#include #include #include #include #include #define NOSTATE 0 #define COPYIN 1 #define COPYOUT 2 #define DIRECTORY 3 #define INFO 4 #define REMOVE 5 #define MAKEDIR 6 #define RENAME 7 #define TRUE 1 #define FALSE 0 #define MAXLEVELS 20 #define BUFFERSIZE 1024 /* vvvvvvvvvvvvvvvv only handle 12-bit FAT entries (sorry, PC-AT) */ #define MAXCLUSTERS 4085 #define FATSIZE 6144 /* ^^^^^^^^^^^^ only handle 12-bit FAT entries (sorry, PC-AT) */ /* functions fatent() and fatmod() also assume 12-bit FAT entries */ #define RECURSIVE TRUE #define NONRECURSIVE FALSE #define IS_DIRECTORY 0x10 #define IS_VOLID 0x8 #define IS_HIDDEN 0x02 #define ATTR_FILE 0x20 #define ATTR_DIR 0x10 #define LASTCLUST 0xfff #define EMPTY 0xe5 #define MAXCLUST 0xff6 #define CTLZ 0x1a #define SEPARATOR '/' #define REVNUMBER "2.2" #define errorout(s) errorout2(s,"") #define badname errorout2("bad MS-DOS filename: ",start) #define badext errorout2("bad MS-DOS filename extension: ",dotpos) #define notmsdos errorout("not MS-DOS media") #define disk2big errorout("disk too big") #define nomem errorout("memory allocate failed") #define bigsector errorout("sector size too large") #define filexist errorout("dest file already exists") #define noroom errorout("no room on MS-DOS disk") #define noopen errorout2("cannot open source file: ",sys_errlist[errno]) #define nocreate errorout2("cannot create dest file: ",sys_errlist[errno]) #define isdir errorout("cannot remove directory w/o -r option") #define flushcr { if (foundcr) putc('\015',filestream); foundcr=0; } /* Version 2.2, 11/8/85 09:48:27 */ char *progname,*basename; char *legalchars="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$&#%'()-@^{}~`!"; extern char *sys_errlist[]; int binary=FALSE; int hidden=FALSE; int output_hidden=FALSE; unsigned char diskbuffer[BUFFERSIZE]; unsigned char fat[FATSIZE]; struct pathstruct { char *filename; unsigned char direntry[32]; int dir_sector,dir_offset; int cluster; } path[MAXLEVELS+1],*pathpointer=path,*namepointer=NULL; int diskdes; FILE *filestream; char oem[8]; int bytes_per_sector, sectors_per_cluster, reserved_sectors, number_of_fats, dir_size, disk_size, media_descriptor, fat_size, num_clusters, dir_start, dir_length; main(argc,argv) char *argv[]; int argc; { int state=NOSTATE,found_file,recurse=NONRECURSIVE,dir_sector; int start_cluster, listfat=FALSE, longdir=FALSE; char *colonpos, *diskname=NULL, *filename=NULL, *strrchr(); char *copy(), *newname=NULL, *checkname(); struct pathstruct *parsefilenames(); long copyout(),file_size; struct tm *timedata; FILE *fopen(); progname=argv[0]; if ((basename=strrchr(progname,'/'))==NULL) basename=progname; else basename++; path[0].filename=NULL; if (!strcmp(basename,"msdoscp")) parsecp(argc,argv,&state,&diskname,&filename); else if (!strcmp(basename,"msdosls")) parsels(argc,argv,&state,&recurse,&longdir,&diskname); else if (!strcmp(basename,"msdosinfo")) parseinfo(argc,argv,&state,&listfat,&diskname); else if (!strcmp(basename,"msdosrm")) parserm(argc,argv,&state,&recurse,&diskname); else if (!strcmp(basename,"msdosmkdir")) parsemkdir(argc,argv,&state,&diskname); else if (!strcmp(basename,"msdosrename")) parserename(argc,argv,&state,&diskname,&newname); else usage(NOSTATE); diskname=copy(diskname); if (newname!=NULL) newname=checkname(copy(newname)); separate(diskname,&colonpos,':'); if (*colonpos==SEPARATOR) colonpos++; if (state==INFO && *colonpos!='\0') usage(state); if ((state==COPYIN || state==COPYOUT || state==REMOVE || state==MAKEDIR || state==RENAME) && *colonpos=='\0') usage(state); if (*colonpos!='\0') namepointer=parsefilenames(colonpos,0); if (state==COPYOUT || state==REMOVE || state==MAKEDIR || state==RENAME) diskdes=open(diskname,O_RDWR); else diskdes=open(diskname,O_RDONLY); if (diskdes==-1) errorout2("failed to open disk device: ",sys_errlist[errno]); readbuff(diskbuffer,0,BUFFERSIZE); set_disk_params(); readfat(); if (state==INFO) { print_disk_params(); if (listfat) printfat(); exit(0); } if (namepointer!=NULL) found_file=search_path(); if (state==COPYIN) { if (!found_file) notfound(); if (!strcmp(filename,"-")) filestream=stdout; else if ((filestream=fopen(filename,"w"))==NULL) nocreate; copyin(namepointer->direntry); exit(0); } if (state==REMOVE) { if (!found_file) notfound(); if (!remove(namepointer->direntry,namepointer->dir_sector, namepointer->dir_offset,recurse)) fprintf(stderr,"%s: remove incomplete because of hidden files\n", progname); exit(0); } if (state==COPYOUT) { if (!strcmp(filename,"-")) filestream=stdin; else if ((filestream=fopen(filename,"r"))==NULL) noopen; if (found_file) filexist; if (pathpointer!=namepointer) notfound(); pathpointer--; dir_sector=new_entry(); file_size=copyout(&start_cluster); update_fat(); update_directory(dir_sector,namepointer->filename, file_size, start_cluster); exit(0); } if (state==MAKEDIR) { if (found_file) filexist; if (pathpointer!=namepointer) notfound(); pathpointer--; dir_sector=new_entry(); makedir(&start_cluster,&timedata); update_fat(); add_directory(dir_sector,namepointer->filename, start_cluster, timedata); exit(0); } if (state==RENAME) { if (!found_file) notfound(); hidden=TRUE; if (checkdupname(newname)) filexist; rename(newname); exit(0); } if (state==DIRECTORY) { if (namepointer==NULL) { dump_directory(dir_start,dir_length,0,"",recurse,longdir); exit(0); } if (!found_file) notfound(); if (namepointer->direntry[11] & IS_DIRECTORY) { int cluster=clust(namepointer->direntry); int start=sect_clust(cluster); dump_directory(start,sectors_per_cluster,cluster,"",recurse,longdir); } else print_file_info("",namepointer->direntry,longdir); exit(0); } } char *copy(c) char *c; { char *result,*malloc(),*strcpy(); if (c==NULL) return NULL; if((result=malloc((unsigned)(strlen(c)+1)))==NULL) nomem; return strcpy(result,c); } printfat() { int i; for (i=0 ; i<=num_clusters+1 ; i++) printf("%d: %d\n",i,fatent(i)); } update_directory(dir_sector,filename, file_size, start_cluster) char *filename; int dir_sector, start_cluster; long file_size; { long time(),clock; struct tm *timedata,*localtime(); int attribute=ATTR_FILE; if (output_hidden) attribute|=IS_HIDDEN; time(&clock); timedata=localtime(&clock); readbuff(diskbuffer,dir_sector,bytes_per_sector); new_dir_entry(filename,file_size,start_cluster,attribute,timedata); writebuff(diskbuffer,dir_sector,bytes_per_sector); } add_directory(dir_sector,filename,start_cluster,timedata) int dir_sector,start_cluster; char *filename; struct tm *timedata; { readbuff(diskbuffer,dir_sector,bytes_per_sector); new_dir_entry(filename,(long)0,start_cluster,ATTR_DIR,timedata); writebuff(diskbuffer,dir_sector,bytes_per_sector); } makedir(start_cluster,timedata) int *start_cluster; struct tm **timedata; { int i,first_sector,parent_cluster=0; long time(),clock; struct tm *localtime(); time(&clock); *timedata=localtime(&clock); fatmod(*start_cluster=new_cluster(2),LASTCLUST); for (i=0 ; icluster; new_dir_entry(".. ",(long)0,parent_cluster,ATTR_DIR,*timedata); writebuff(diskbuffer,first_sector,bytes_per_sector); } new_dir_entry(filename,file_size,start_cluster,attributes,timedata) char *filename; int start_cluster, attributes; struct tm *timedata; long file_size; { int i,j; for (i=0 ; diskbuffer[i]!='\0' && diskbuffer[i]!=EMPTY ; i+=32); for (j=0 ; j<11 ; j++) diskbuffer[i+j]=filename[j]; diskbuffer[i+11]=attributes; diskbuffer[i+0x17]=timedata->tm_hour<<3 | timedata->tm_min>>3; diskbuffer[i+0x16]=(timedata->tm_min & 7)<<5 | timedata->tm_sec/2; diskbuffer[i+0x19]=((timedata->tm_year+20) % 100) <<1 | (timedata->tm_mon+1)>>3; diskbuffer[i+0x18]=((timedata->tm_mon+1) & 7)<<5 | timedata->tm_mday; diskbuffer[i+0x1a]=start_cluster & 0xff; diskbuffer[i+0x1b]=start_cluster >> 8; diskbuffer[i+0x1c]=file_size & 0xff; diskbuffer[i+0x1d]=file_size >> 8 & 0xff; diskbuffer[i+0x1e]=file_size >> 16 & 0xff; diskbuffer[i+0x1f]=file_size >> 24 & 0xff; } new_entry() { if (namepointer==path) return find_vacancy(dir_start,dir_length,0); else { int cluster=clust(pathpointer->direntry); int start=sect_clust(cluster); int length=sectors_per_cluster; return find_vacancy(start,length,cluster); } } find_vacancy(start,length,cluster) int start,length,cluster; { unsigned char sectorbuffer[BUFFERSIZE]; while ((length--)>0) { int j=0; readbuff(sectorbuffer,start,bytes_per_sector); while (j0) { int i; readbuff(sectorbuffer,sector,bytes_per_sector); for (i=0; i0) { readbuff(diskbuffer,start++,bytes_per_sector); for (j=0 ; j w/o */ if (ch=='\012') { putc('\n',filestream); foundcr=0; } else if (ch=='\015') { flushcr ; foundcr=1; } else { flushcr ; putc(ch,filestream); } } } if (length==0) { if((cluster=fatent(cluster))>MAXCLUST) return; start=sect_clust(cluster); length=sectors_per_cluster; } } } notfound() { char *f_name(); errorout(f_name("could not find: ",(pathpointer->filename))); } rename(newname) char *newname; { int i,pos=namepointer->dir_offset; readbuff(diskbuffer,namepointer->dir_sector,bytes_per_sector); for (i=0; i<11; i++) diskbuffer[pos+i]=newname[i]; writebuff(diskbuffer,namepointer->dir_sector,bytes_per_sector); } checkdupname(newname) char *newname; { struct pathstruct newpath[2]; int cluster; newpath[0].filename=newname; newpath[1].filename=NULL; pathpointer=newpath; if (namepointer==path) return find_file(dir_start,dir_length,0); pathpointer=namepointer; cluster=(--pathpointer)->cluster; pathpointer=newpath; return find_file(sect_clust(cluster),sectors_per_cluster,cluster); } search_path() { return find_file(dir_start,dir_length,0); } find_file(start,length,cluster) int start,length,cluster; { int j,k,start_cluster; unsigned char sectorbuffer[BUFFERSIZE]; if (pathpointer->filename==NULL) return TRUE; while ((length--)>0) { readbuff(sectorbuffer,start,bytes_per_sector); for (j=0 ; jfilename,11) && !(sectorbuffer[j+11] & IS_VOLID) && !(!hidden && sectorbuffer[j+11] & IS_HIDDEN)) { for (k=0 ; k<32 ; k++) pathpointer->direntry[k]=sectorbuffer[j+k]; pathpointer->dir_sector=start; pathpointer->dir_offset=j; (pathpointer++)->cluster=start_cluster=clust(§orbuffer[j]); if (pathpointer->filename!=NULL && !(sectorbuffer[j+11] & IS_DIRECTORY)) return pathpointer--,FALSE; return find_file(sect_clust(start_cluster), sectors_per_cluster, start_cluster); } if (j0) { readbuff(sectorbuffer,start,bytes_per_sector); for (j=0 ; j",fnm); printf("%-39s",dirline); free(dirline); } else printf("%s%c\n",fnm,SEPARATOR); } else { if (longdir) printf("%-39s",fnm); else printf("%s\n",fnm); } free(fnm); if (!longdir) return; print_date_time(direntry); printf("\t%ld (%d)\n",file_size(direntry),clust(direntry)); } long file_size(direntry) unsigned char *direntry; { return ((long)direntry[28]) + ((long)direntry[29]<<8) + ((long)direntry[30]<<16) + ((long)direntry[31]<<24); } print_date_time(direntry) unsigned char *direntry; { int hr,mn,sc,yr,mo,dy; hr=direntry[23]>>3; mn=(direntry[23] & 0x7) <<3 | direntry[22]>>5; sc=(direntry[22] & 0x1f) * 2; yr=(direntry[25]>>1) + 1980; mo=(direntry[25] & 0x1) <<3 | direntry[24]>>5; dy=direntry[24] & 0x1f; printf("\t%02.2d/%02.2d/%d %02.2d:%02.2d:%02.2d",mo,dy,yr,hr,mn,sc); } clust(direntry) unsigned char *direntry; { return direntry[26] | direntry[27]<<8; } readfat() { readbuff(fat,reserved_sectors,fat_size*bytes_per_sector); } readbuff(buffer,sector,length) unsigned char *buffer; int sector,length; { int i; long lseek(); if (lseek(diskdes,(long) (sector*bytes_per_sector),0)==-1) errorout2("seek fail: ",sys_errlist[errno]); if ((i=read(diskdes,buffer, length))==-1) errorout2("read fail: ",sys_errlist[errno]); if (i>4 | fat[i+1] <<4); } new_cluster(entry) int entry; { while (entry<=num_clusters+1) if (fatent(entry++)==0) return --entry; noroom; } update_fat() { int i; for (i=reserved_sectors; i>8 & 0xf; } else { fat[i]=fat[i] & 0xf | value <<4 & 0xf0; fat[i+1]=value >>4 & 0xff; } return j; } set_disk_params() { char *strncpy(); strncpy(oem,diskbuffer+3,8); bytes_per_sector=diskbuffer[11] | diskbuffer[12]<<8; if (countbits(bytes_per_sector)!=1) notmsdos; sectors_per_cluster=diskbuffer[13]; reserved_sectors=diskbuffer[14] | diskbuffer[15]<<8; number_of_fats=diskbuffer[16]; dir_size=diskbuffer[17] | diskbuffer[18]<<8; disk_size=diskbuffer[19] | diskbuffer[20]<<8; media_descriptor=diskbuffer[21]; fat_size=diskbuffer[22] | diskbuffer[23]<<8; dir_start=reserved_sectors+fat_size*number_of_fats; dir_length=dir_size*32/bytes_per_sector; if (dir_size*32 % bytes_per_sector != 0) notmsdos; num_clusters=(disk_size-dir_start-dir_length)/sectors_per_cluster; if (num_clusters>MAXCLUSTERS) disk2big; /* don't handle 16-bit FAT entries */ if (dir_start+dir_length>disk_size) notmsdos; if (bytes_per_sector>BUFFERSIZE) bigsector; } countbits(n) int n; { int b; for (b=0 ; n!=0 ; n>>=1) if (n & 0x1) b++; return(b); } print_disk_params() { int freespc=0,i=2; printf("OEM ID = '%8.8s'\n",oem); printf("Sector size = %d bytes\n",bytes_per_sector); printf("Cluster size = %d sectors\n",sectors_per_cluster); printf("Number of reserved sectors = %d\n",reserved_sectors); printf("Number of FATs = %d\n",number_of_fats); printf("Number of directory entries = %d\n",dir_size); printf("Size of disk = %d sectors\n",disk_size); printf("Media descriptor = %2.2x\n",media_descriptor); printf("Size of FAT = %d sectors\n\n",fat_size); printf("Directory starts at sector %d\n",dir_start); printf("Length of directory is %d sectors\n",dir_length); printf("Number of clusters = %d\n",num_clusters); printf("Total file space on disk = %ld bytes\n", (long)num_clusters*(long)sectors_per_cluster*(long)bytes_per_sector); while (i<=num_clusters+1) freespc+=fatent(i++)==0; printf("Free file space on disk = %ld bytes\n\n", (long)freespc*(long)sectors_per_cluster*(long)bytes_per_sector); } struct pathstruct *parsefilenames(start,level) char *start; int level; { char *slashpos,*checkname(); if (level==MAXLEVELS) errorout("path too long"); separate(start,&slashpos,SEPARATOR); path[level++].filename=checkname(start); path[level].filename=NULL; if (*slashpos!='\0') return(parsefilenames(slashpos,level)); return(&path[level-1]); } char *checkname(start) char *start; { char *dotpos,*msdospad(); separate(start,&dotpos,'.'); if (checkrange(start,8)) badname; if (checkrange(dotpos,3)) badext; if (strlen(start)==0 || strlen(start)>8) badname; if (strlen(dotpos)>3) badext; return msdospad(start,dotpos); } char *msdospad(a,b) char *a,*b; { char *res,*malloc(),*strcpy(); int i; if ((res=malloc(12))==NULL) nomem; strcpy(res,a); for (i=strlen(res) ; i<8 ; i++) res[i]=' '; for (i=0 ; b[i]!='\0' ; i++) res[i+8]=b[i]; for (i=strlen(res) ; i<11 ; i++) res[i]=' '; res[11]='\0'; return res; } separate(a,b,c) char *a,**b,c; { char *strchr(); *b=strchr(a,c); if (*b==NULL) for (*b=a ; **b!='\0' ; (*b)++) ; else *(*b)++='\0'; } checkrange(start,maxch) char *start; int maxch; { while (*start!='\0' && (maxch--)>0) { if (islower(*start)) *start+=('A'-'a'); *start&=0x7f; if (!strspn(start++,legalchars)) return TRUE; } return FALSE; } usage(state) int state; { if (state==COPYIN || state==COPYOUT) { fprintf(stderr,"Usage: %s [-b|t] [-h] :\n", progname); fprintf(stderr," %s [-b|t] [-h] : \n", progname); } else if (state==DIRECTORY) fprintf(stderr,"Usage: %s [-lrh] [:]\n",progname); else if (state==REMOVE) fprintf(stderr,"Usage: %s [-rh] :\n",progname); else if (state==INFO) fprintf(stderr,"Usage: %s [-f] \n",progname); else if (state==MAKEDIR) fprintf(stderr,"Usage: %s :\n",progname); else if (state==RENAME) fprintf(stderr,"Usage: %s [-h] : \n",progname); else { fprintf(stderr,"MS-DOS Utilities, Rev. %s\n",REVNUMBER); fprintf(stderr,"Unrecognized invocation, use:\n"); fprintf(stderr," msdoscp\n msdosinfo\n msdosls\n"); fprintf(stderr," msdosmkdir\n msdosrename\n msdosrm\n"); } exit(1); } parsecp(argc,argv,state,diskname,filename) int argc,*state; char *argv[],**diskname,**filename; { char *strchr(); while (--argc>0) { if (**++argv=='-') { char ch; if (*++*argv=='\0') { if (*state==NOSTATE) { *state=COPYOUT; *filename="-"; } else if (*state==COPYIN && *filename==NULL) *filename="-"; else usage(COPYIN); } else while ((ch=(*(*argv)++))!='\0') { if (ch=='b') binary=TRUE; else if (ch=='t') binary=FALSE; else if (ch=='h') hidden=TRUE; else usage(COPYIN); } } else { if (*filename!=NULL && *diskname!=NULL) usage(COPYIN); if (*state==NOSTATE) { if (strchr(*argv,':')==NULL) { *state=COPYOUT; *filename=(*argv); } else { *state=COPYIN; *diskname=(*argv); } } else if (*state==COPYIN) *filename=(*argv); else *diskname=(*argv); } } if (*diskname==NULL || *filename==NULL) usage(COPYIN); if (strchr(*diskname,':')==NULL) usage(COPYIN); if (strchr(*filename,':')!=NULL) usage(COPYIN); if (*state==COPYOUT) { output_hidden=hidden; hidden=TRUE; } } parsels(argc,argv,state,recurse,longdir,diskname) int argc,*state,*recurse,*longdir; char *argv[],**diskname; { *state=DIRECTORY; while (--argc>0) { if (**++argv=='-') { char ch; if (*++*argv=='\0') usage(*state); while ((ch=(*(*argv)++))!='\0') { if (ch=='r') *recurse=TRUE; else if (ch=='l') *longdir=TRUE; else if (ch=='h') hidden=TRUE; else usage(*state); } } else if (*diskname!=NULL) usage(*state); else *diskname=(*argv); } if (*diskname==NULL) usage(*state); } parseinfo(argc,argv,state,listfat,diskname) int argc,*state,*listfat; char *argv[],**diskname; { *state=INFO; while (--argc>0) { if (**++argv=='-') { char ch; if (*++*argv=='\0') usage(*state); while ((ch=(*(*argv)++))!='\0') { if (ch=='f') *listfat=TRUE; else usage(*state); } } else if (*diskname!=NULL) usage(*state); else *diskname=(*argv); } if (*diskname==NULL) usage(*state); } parserm(argc,argv,state,recurse,diskname) int argc,*state,*recurse; char *argv[],**diskname; { *state=REMOVE; while (--argc>0) { if (**++argv=='-') { char ch; if (*++*argv=='\0') usage(*state); while ((ch=(*(*argv)++))!='\0') { if (ch=='r') *recurse=TRUE; else if (ch=='h') hidden=TRUE; else usage(*state); } } else if (*diskname!=NULL) usage(*state); else *diskname=(*argv); } if (*diskname==NULL) usage(*state); } parsemkdir(argc,argv,state,diskname) int argc,*state; char *argv[],**diskname; { *state=MAKEDIR; hidden=TRUE; if (argc!=2) usage(MAKEDIR); *diskname=argv[1]; } parserename(argc,argv,state,diskname,newname) int argc,*state; char *argv[],**diskname,**newname; { *state=RENAME; while (--argc>0) { if (**++argv=='-') { char ch; if (*++*argv=='\0') usage(*state); while ((ch=(*(*argv)++))!='\0') { if (ch=='h') hidden=TRUE; else usage(*state); } } else if (*diskname!=NULL) { if (*newname!=NULL) usage(*state); else *newname=(*argv); } else *diskname=(*argv); } if (*diskname==NULL || *newname==NULL) usage(*state); } errorout2(msg1,msg2) char *msg1,*msg2; { fprintf(stderr,"%s: %s%s%s\n",progname,msg1,msg2); exit(1); }