/*                              HRSDOS (c) by Robert Gault Hrsdos is a utility to permit file transfer between OS-9 and RSDOS in either a floppy disk or hard disk environment. The program has been designed for RGB DOS type hard drives and may or may not work with others.  The support program SPECS.BAS is used to supply drive offset values for a data file /dd/sys/hrs_data which must be installed for successful hard drive use.  It is NOT necessary to use a patched cc3disk or pre-read a normal floppy for floppy transfers.  HISTORY:      current version v1.0.1 Oct 21, 1991              v1.0.0 Oct  1,1991: hard drive action only              v1.0.1 Oct 10,1991: added floppy action with patched cc3disk RG                     Oct 20,1991: removed need for patched cc3disk         RG              v1.0.2 Oct 25,1991: increased size of I/O buffer to 1 gran   RG                     Oct 27,1991: trimmed unused variables; made some global                                  into local                               RG                                  cleaned up setup(); changed floppy                                  preread into SS.Frz                      RG                     Nov 26,1991: corrected gran calc. for > 23            RG                                  floppy preread reinstated; SS.Frz removed RG              v1.0.3 May 25,1992: corrected conversion to lower for /dr#    RG                                  NOTE: clib.new does not work correctly                                   with readln() ;seems to return -1 for                                   EOF.                    Sept 26,1994: See NOTE below.              v1.1.0 Jan 31,1994: problems with interrupt polling table    RG                                  corrected by adding an I$Attach call to                                  device.              v1.1.1 June05,1994: corrected combined -b -a attributes      RG              v1.1.2 Sept25,1994: corrected writes of directory entries    RG                                  now second line is blanked.                                  Now uses Kreider clib.l; repaired setup()                                  which did not correctly assign specs; was                                  pointer, now is specs[40].              v1.1.3 Oct.13,1994: correct -dir so that only ascii printed  RG              v1.1.4 Nov.18,1995: changed attributes to accept -abcd format RG                     Nov.25,1995: corrected calc. of drive table block     RG                     Nov.26,1995: made drive access single user  RG                     May 22,1997: Correct put() duplicate name error RG                                  Correct put() duplicate last sector RG*/#include "/h2/defs/stdio.h"#include "/h2/defs/ctype.h"#include "/h2/defs/os9.h"#define TRUE 1#define FALSE 0struct registers reg;char filetype,fileformat,dev_path,RS_path,OS_path;char drv_num[4],ext[4],RSnm[9],device[9],RSfile[11],RSname[16],OSname[32];int drive,flag;unsigned *grn_size;long directory,gat,gran0,grn_loc,OS_size;/* buffer large enough for one gran (9 sectors) */char dir_entry[32],sysDAT[64],gat_buf[256],buffer[2304];#asm fcc "**** hrsdos V1.20 (c) May.  1997 by Robert Gault ****"#endasmmain(argc,argv)int argc;char **argv;{ char cmd,drv[10]; int i; if(!(--argc)) usage (1); if(**(++argv)!='-') usage (2); /* if you don't have a - */ argc--; if(strcmp(*argv,"-")==0) usage(2); /* if you only have a - */ if(strcmp(*argv,"-?")==0) help(); setlower(*argv); cmd=findstr(1,"dirgetputdel",(*argv)+1); switch(cmd) {  case 1 : cmd=1;   break;  case 4 : cmd=2;   break;  case 7 : cmd=3;   break;  case 10 : cmd=4;   break;  default : usage(3);  } argv++; argc--; if (**argv=='-' && cmd!=3) usage(4); filetype=0; fileformat=0; while (**argv=='-') {  int temp;  setlower(*argv);  temp=findstr(1,"-a-b-d-m-t-ba-da-ta",*argv);  switch (temp)   {   case  1 : fileformat=0xff; /* ASCII; default non-ASCII*/    break;   case  3 : filetype=0; /* token ; default*/    break;   case  5 : filetype=1; /* data */    break;   case  7 : filetype=2; /* machine */    break;   case  9 : filetype=3; /* text */    break;   case 11 : filetype=0;    fileformat=0xff;    break;   case 14 : filetype=1;    fileformat=0xff;    break;   case 17 : filetype=3;    fileformat=0xff;    break;   default : usage(4);    }  argc--;  argv++;  } if(filetype==2 && fileformat==0xff) usage(4); if(**argv!='/') usage(6); reg.rg_x=*argv; if(_os9(F_PrsNam,&reg)) usage(6); strncpy(device,*argv,reg.rg_b+1); setlower(device); /* make device into lower case */ reg.rg_x=device+1; reg.rg_a=0x03;    /* update */ if(_os9(I_Attach,&reg)==-1) usage(28); strcat(device,"@"); if((dev_path=open(device,0x43))==-1) usage(7); getstat(0,dev_path,buffer); if(buffer[3]!=0x80)  flag=FALSE; /* floppy */ else  flag=TRUE; /* hard drive */ switch (cmd) {  case  1 : if(argc!=1 && flag) usage (5);   break; /* hard drive must specify partition */  case  2 : if(argc!=2) usage (5);   break;  case  3 : if(argc!=2) usage (5);   break;  case  4 : if(argc!=1) usage (5);   break;  } argc--; argv++; switch(cmd) {  case 1: if(flag)   {       /* hard drive so look for ':#' */    strcpy(drv_num,*argv);    testdrv();    }   break; /* if dir but not hard drive; just quit after no action */  default: strcpy(RSname,*argv);   testRS();   break;  } if(argc>0) {  argv++;  strcpy(OSname,*argv);  } setup(); switch (cmd) {  case  1 : dir();   break;  case  2 : get();   break;  case  3 : put();   break;  case  4 : del();   break;  } }setlower(string)char *string;{ while ( *string ) {  *string=tolower(*string);  string++;  } }testdrv(){ if(*drv_num!=':') usage (8); drive=atoi(drv_num+1); } testRS(){ int i,j; int loc1, loc2; RSnm[8]=ext[3]='\0'; if(!(loc1=findstr(1,RSname,"."))) loc1=findstr(1,RSname,"/"); if(loc1==0) {  loc1=strlen(RSname)+1;  strcat(RSname,".   ");  } if(loc1>9) usage (9); if(!(loc2=findstr(loc1,RSname,":")) && flag) usage (8); if(!loc2) {  strcat(RSname,":");  loc2=strlen(RSname);  } if(loc2-loc1>4) usage(10); if(flag) drive=atoi(RSname+loc2); strncpy(RSnm,RSname,loc1-1); if(loc1<9) strncat(RSnm,"        ",9-loc1); for(j=0,i=loc1;j<(loc2-loc1-1);i++,j++)  ext[j]=RSname[i]; if(loc2-loc1<4) strncat(ext,"   ",4-(loc2-loc1)); strncpy(RSfile,RSnm,8); strncat(RSfile,ext,3); }flsize(j)   /* count number of grans in RS file */char j; { int i; for(i=1;i<69;i++)  if((j=gat_buf[j])<0) break; return(i); } find(){ int i; char gran1; dir_entry[0]='\0'; /* preset flag on name */ for(i=1;i<69;i++) {  if(read(dev_path,dir_entry,32)!=32) usage(11);  if('\xff'== dir_entry[0]) usage(12);  if('\0'!= dir_entry[0])  {   if(!strncmp(RSfile,dir_entry,11))   {    gran1=dir_entry[13];    grn_size=&dir_entry[14];    break;    }   }  } }usage(err)int err;{ char *rmsg; puts("Use: hrsdos (-? -dir -get -put -del) device [RSDOS_file] [OS9_file]"); switch(err) {  case 1: rmsg="not enough arguments";   break;  case 2: rmsg="-cmd; command required";   break;  case 3: rmsg="unknown command";   break;  case 4: rmsg="invalid command modifier";   break;  case 5: rmsg="incorrect number of arguments";   break;  case 6: rmsg="invalid device name";   break;  case 7: rmsg="cannot open device";   break;  case 8: rmsg="incorrect drive# format; :#";   break;  case 9: rmsg="incorrect RS file name";   break;  case 10: rmsg="incorrect RS extension; {(.)(/)ext}";   break;  case 11: rmsg="cannot read RS directory";   break;  case 12: rmsg="RS file not on specified drive";   break;  case 13: rmsg="data missing; /dd/sys/hrs_data";   break;  case 14: rmsg="system memory error";   break;  case 15: rmsg="cannot access GAT table";   break;  case 16: rmsg="cannot access RS directory";   break;  case 17: rmsg="cannot create OS9 file";   break;  case 18: rmsg="cannot seek RS file sector";   break;  case 19: rmsg="cannot access os9 file";   break;  case 20: rmsg="OS9 file too large for floppy disk";   break;  case 21: rmsg="not enough free disk space";   break;  case 22: rmsg="RS drive read error";   break;  case 23: rmsg="missing #offset; /dd/sys/hrs_data";   break;  case 24: rmsg="cannot create RS file";   break;  case 25: rmsg="preseek to GAT failed";   break;  case 26: rmsg="preread of GAT failed";   break;  case 27: rmsg="cannot read RS file sector";   break;  case 28: rmsg="cannot attach device";   break;  case 29: rmsg="cannot access the path descriptor";   break;  case 99: rmsg="";   puts("  Before using this program, you must first install /dd/sys/hrs_data");   puts("Use your favorite editor or BUILD to create the file. The file must");   puts("include all hard drives in the system, including /dd if it is a hard");   puts("drive. Drive order in the file is unimportant.");   puts("  Format: drive name [space(s)] [#]RGB_offset");   puts("         # is required");   puts("");   puts("  Obtain the offset value by running the RSDOS program SPECS.BAS for");   puts("each hard drive in the system. SPECS.BAS is of course used under");   puts("RGB-DOS.");   puts("");   puts("                    Typical hrs_data file");   puts("/dd #32760");   puts("/h0 #32760");   puts("/h2 #268380");   break;  } puts(rmsg); exit(0); } help(){ puts("Use: hrsdos (-? -dir -get -put -del) device [RSDOS_file] [OS9_file]"); puts(" required: command and device name of hard disk"); puts(" required with -dir: instead of RSDOS_file use :#"); puts("         where ex.  :12  is drive 12 of RSDOS section"); puts(" required with -get/-put: RSDOS_file OS9_file_path"); puts(" required with -del: RSDOS_file"); puts(" RSDOS_file format: name(.or/)ext:drive#_hard_drive"); puts("    hard drive is assumed to have section of RSDOS type drives"); puts(""); puts("Commands:"); puts(" -?     you know this one"); puts(" -dir   for directory of hard drive RSDOS drive"); puts(" -get   transfer RSDOS file to OS9 section"); puts(" -put   transfer OS9 file to RSDOS section"); puts(" -del   delete RSDOS file"); puts(""); puts("Options: for use with -put"); puts(" -b     type 0; BASIC tokenized file; default"); puts(" -d     type 1; BASIC data file"); puts(" -m     type 2; Machine language program"); puts(" -t     type 3; Text editor file"); puts(" -a     ASCII indicator; default is non-ASCII"); exit(); }setup(){ int i,k,data_path,prID,io_block; unsigned offset,io_byte,table_block,ui; char *j,specs[40],drv[10],*blk_buf; char drv_siz[3],drv_cyl[3]; long atol(),drv_bytes,drv_tracks,base; pflinit();if(flag){ data_path=open("/dd/sys/hrs_data",1); if(data_path==-1) usage(99);  /* explain about hrs_data */ for(i=0;i<10;i++)  drv[i]='\0'; strncpy(drv,device,strlen(device)-1); for(;;) {  if(readln(data_path,specs,40)<1) usage(13);  setlower(specs);          /* make file entries lower case */  if(findstr(1,specs,drv)) break;  } if((i=findstr(1,specs,"#"))==0) {  close(data_path);  usage(23);  } close(data_path); base=atol(specs+i)<<8; }else{ base=0L; drive=0; } prID=getpid(); /* get process ID of this program */ reg.rg_a=1; reg.rg_x=buffer; _os9(F_GPrDsc,&reg); /* get system process descriptor */ for(i=0;i<64;i++)  sysDAT[i]=buffer[i+0x40]; /* copy system DAT image */ reg.rg_a=prID; _os9(F_GPrDsc,&reg); /* get process descriptor for this program */ io_block=0xff&buffer[0x30+dev_path]; /* get path block number from I/O table */#asm leax sysDAT,y stx reg+1,y    regD = regX#endasm reg.rg_x=0x88;  /* D.PthDBT; path descriptor block table pointer */ reg.rg_y=2; reg.rg_u=&table_block; _os9(F_CpyMem,&reg); /* get location of I/O block table */ reg.rg_x=table_block; /* get the pointer into register X */ reg.rg_y=64;          /* length of a block in table */ reg.rg_u=buffer; _os9(F_CpyMem,&reg); /* get first block of table *//* io_block contains the real system path number of the process I/O path;  divide it by 4 as there are four descriptors per block. Now get the MSB  of the descriptor from the table header and calculate an offset. */ io_byte=0xff&buffer[(int)(io_block/4)]; /* get address of path descriptor */ offset=(io_byte<<8)+((io_block%4)<<6); /* pointer to path descriptor */ reg.rg_x=offset; reg.rg_y=64; _os9(F_CpyMem,&reg); /* get correct block */ if((0xff&buffer[0])!=io_block) usage(29); /* buffer now contains copy of path descriptor */ gran0=base+drive*161280L; gat=gran0+78592L; directory=gat+256L; drv_bytes=((drive+1)*161280L)+base; drv_tracks=drv_bytes/(((0xff&buffer[0x29])<<8)+(0xff&buffer[0x2a])); /* change path descriptor to permit special read/writes */ for(i=0,j=&drv_bytes;i<4;i++,j++)  buffer[i+0x0f]= *j;      /* change PD.SIZ */ drv_bytes/=256L; ltol3(drv_siz,&drv_bytes,1); ltol3(drv_cyl,&drv_tracks,1); for(i=0;i<3;i++)  buffer[i+0x19]=drv_siz[i];  /* change PD.SSZ segment size = total sectors */ for(i=0;i<2;i++)  buffer[i+0x25]=drv_cyl[i+1];  /* change PD.CYL number of tracks */ buffer[0x27]=1;              /* one side */ buffer[0x24]=1;              /* double density */ /* path descriptor image now fully modified;  map in real path descriptor */ reg.rg_x=sysDAT[(offset/0x2000)*2+1]; /* calc. system block #; convert to  pointer in table which consists of 16bit words never larger than $FF;  then find true MMU block number from sysDAT. */ reg.rg_b=1; if(_os9(F_MapBlk,&reg)) usage(14); blk_buf=reg.rg_u; ui=offset%0x2000; for(i=0;i<64;i++)  *(blk_buf+i+ui)=buffer[i];  /* copy image into real path descriptor                              and unmap path descriptor*/ reg.rg_u=sysDAT[(offset/0x2000)*2+1]; reg.rg_b=1; _os9(F_ClrBlk,&reg); if(!flag)    /* if floppy setup fake drive table */ {  lseek(dev_path,0l,0);  read(dev_path,gat_buf,256);  /* map in drive table */  offset=((0xff&buffer[0x1e])<<8)+(0xff&buffer[0x1f]);  reg.rg_x=sysDAT[(offset/0x2000)*2+1];  reg.rg_b=1;  if(_os9(F_MapBlk,&reg)) usage(14);  blk_buf=reg.rg_u;  ui=offset%0x2000;  /* modify drive table to correct values for 35T 18S 256B 1side */  *(blk_buf+ui)=*(blk_buf+0x11+ui)=0;  /* drive size */  *(blk_buf+1+ui)=buffer[0x1a];  *(blk_buf+2+ui)=buffer[0x1b];  *(blk_buf+3+ui)=*(blk_buf+0x12+ui)=buffer[0x2a]; /* track size */  *(blk_buf+0x0d+ui)=7;               /* attributes */  *(blk_buf+0x10+ui)=2;               /* media format */  *(blk_buf+0x15+ui)=0;               /* clear track MSB */  /* unmap drive table */  reg.rg_u=sysDAT[(offset/0x2000)*2+1];  reg.rg_b=1;  _os9(F_ClrBlk,&reg);/*  reg.rg_a=dev_path;  reg.rg_b=SS_Frz;     this would be best best but freeze needs driver mod.  _os9(I_SetStt,&reg); */  } /* path descriptor now is set to read hard/floppy drive out to end of    indicated RSdrive section. */ if(lseek(dev_path,gat,0)==-1) usage(25); if(read(dev_path,gat_buf,256)<1) usage(26); /* ready to read directory sector #1 */ } dir(){ int i,j,k; char ASC; dir_entry[0]='\0'; /* preset flag on name */ for(i=1;i<69;i++) {  if(read(dev_path,dir_entry,32)!=32) usage(16);  if('\xff'== dir_entry[0]) break;  if('\0'!= dir_entry[0])  {   for(j=0;j<11;j++){    if(dir_entry[j]<0x20 || dir_entry[j]>0x7E)     dir_entry[j]=0x2E;    }   for(j=0;j<8;j++)    printf("%c",dir_entry[j]);   printf(" ");   for(j=0;j<3;j++)    printf("%c",dir_entry[j+8]);   if('\xff'== dir_entry[12])    ASC='A';   else    ASC='B';   printf(" %d %c %d\n",dir_entry[11],ASC,flsize(dir_entry[13]));   }  }  for(i=0,j=0;i<68;i++)   if(gat_buf[i]=='\xff') j++;  printf("Free grans: %d\n",j); } get(){ int i,j,k,sectr,lsectr; char *ASC; find(); if(access(OSname,0)==0)  {  printf("OS file already exits; replace file? [ Y or N] ");  gets(ASC);  *ASC=tolower(*ASC);  if(*ASC!='y')  {   puts("file not copied");   exit(0);   }  } if((OS_path=creat(OSname,3))==-1) usage(17); j=dir_entry[13]; sectr=9; for(i=1;i<69;i++) {  if(j>33)   grn_loc=gran0+4608L+2304L*j;  else    grn_loc=gran0+2304L*j;   if(-1L==lseek(dev_path,grn_loc,0)) usage(18);  if(gat_buf[j]&0x80)  {   sectr=(gat_buf[j]&0x0f)-1;   lsectr=*grn_size;   if((k=read(dev_path,buffer,(sectr<<8)+lsectr))==-1) usage(27);   if(write(OS_path,buffer,k)==-1) usage(17);   break;   }  else  {   if((k=read(dev_path,buffer,sectr<<8))==-1) usage(27);   if(write(OS_path,buffer,k)==-1) usage(17);    }  j=gat_buf[j];  } close(OS_path); close(dev_path); }put(){ int i,j,k,size,sectslop,granslop,sectors; long ts0; char glist[68],*ASC; if(access(OSname,1)) usage(19); if(-1==(OS_path=open(OSname,1))) usage(19); if(getstat(2,OS_path,&OS_size)) usage(19); if(OS_size>156672L) usage(20); size=OS_size/2304L; granslop=OS_size%2304L; if(granslop)  size++; /* size is number of grans for file */ sectors=granslop/256; sectslop=granslop%256; if(sectslop)  sectors++; /* sectors is number in last gran */ else {  sectslop=256-1;  sectors=9;  } for(i=j=0;i<68;i++) {  if(gat_buf[i]=='\xff')  {   glist[j++]=i;   }  } if(size>j) usage(21); for(i=1;i<69;i++) {  if(read(dev_path,dir_entry,32)==-1) usage(22);  if(dir_entry[0]=='\xff') break;  if(strncmp(RSfile,dir_entry,11)==0)  {   puts("RS file already exits; file not copied");   exit(0);   }  } if(lseek(dev_path,gat+256L,0)==-1) usage(25); for(i=1;i<69;i++) {  if(read(dev_path,dir_entry,32)==-1) usage(22);  if(dir_entry[0]=='\0') break;  if(dir_entry[0]=='\xff') break;  } if(i==69) usage(22); for(i=0;i<(size-1);i++)  gat_buf[glist[i]]=glist[i+1]; gat_buf[glist[i]]=sectors|0xc0; for(i=0;i<11;i++)  dir_entry[i]=RSfile[i]; dir_entry[11]=filetype; dir_entry[12]=fileformat; dir_entry[13]=glist[0]; dir_entry[14]='\0'; dir_entry[15]=sectslop; for(i=16;i<32;i++)  dir_entry[i]='\0'; lseek(dev_path,-32L,1); write(dev_path,dir_entry,32); lseek(dev_path,gat,0); write(dev_path,gat_buf,256); ts0=gat-78592L; for(i=0;i<size-1;i++) {  if((j=glist[i])>33)   lseek(dev_path,ts0+4608L+2304L*j,0);  else   lseek(dev_path,ts0+2304L*j,0);  if((k=read(OS_path,buffer,2304))==-1) usage(18);  if(write(dev_path,buffer,k)==-1) usage(24);   } if((j=glist[i])>33)   lseek(dev_path,ts0+4608L+2304L*j,0);  else   lseek(dev_path,ts0+2304L*j,0); if((k=read(OS_path,buffer,((sectors-1)<<8)+sectslop))==-1) usage(18); if(write(dev_path,buffer,k)==-1) usage(24); close(OS_path); close(dev_path); } del(){ int i,j; find(); i=0x7f&dir_entry[13]; while(((gat_buf[i])&0x80)==0) {  j=0x7f&gat_buf[i];  gat_buf[i]='\xff';  i=j;  } gat_buf[i]='\xff'; dir_entry[0]='\0'; lseek(dev_path,-32L,1); write(dev_path,dir_entry,32); lseek(dev_path,gat,0); write(dev_path,gat_buf,256); close(dev_path); }