/*      ------------------------------------------------------- 
        *** Template for Procedure Heading *** 
 
        Name: 
        Result: 
        Errors: 
        Globals: 
        Macros: 
        Procedures: 
 
        Action: 
 
        ------------------------------------------------------- */ 
 
/*      : : : : : : : : : : : : : : : : : : : : : : : : : : : : 
 
        ucsd2cpm -- Transfer '.TEXT' files from a UCSD directory 
                    formatted disk to a CP/M directory formatted 
                    disk. 
 
                1) header blocks removed 
                2) linefeeds added after carriage returns 
                3) indents converted to appropriate # of spaces 
                4) filler nulls removed 
                5) control-A's removed  (added 2/13/80 hrm) 
 
		6) fixed to work with 1.4x 'c' compilers
		   by Chris Undery 5th oct '82

		   
        : : : : : : : : : : : : : : : : : : : : : : : : : : : : */ 
 
 
 
 
 
/*      : : : : : : : : : : : : : : : : : : : : : : : : : : : : 
 
                Constants 
 
        : : : : : : : : : : : : : : : : : : : : : : : : : : : : */ 
 
#include bdscio.h
 
#define DLE     0x10            /* Data Link Escape -- indent flag */ 
#define CR      0x0D            /* carriage return */ 
#define LF      0x0A            /* linefeed */ 
#define NULL    0               /* ascii null */ 
#define CPM_EOF 0X1A            /* CP/M ascii endfile mark */ 
 
#define TRUE    1               /* booleans */ 
#define FALSE   0 
 
#define SEL_DSK 14              /* bdos function number */ 
#define SET_DMA 26              /* bdos function number */ 
 
#define SET_TRK 10              /* bios index number (was 9 c.u.) */ 
#define SET_SEC 11              /* bios index number (was 10 c.u) */ 
#define READ    13              /* bios index number (was 12 c.u) */ 
 
#define DUMMY   0               /* dummy parameter for bios routine */ 
 
#define EOF_F   0xFFFF          /* end of file flag */ 
#define D_ENT_SZ 26             /* UCSD directory entry size */ 
#define D_TITLE 6               /* offset to entry title */ 
#define UCSD_NAM_SZ 20          /* size of the name part of ucsd dir entry */ 
#define UCSD_DIR_SZ 2048        /* size of UCSD directory in bytes */ 
 
#define SECT_SIZE 128           /* bytes per physical sector */ 
#define BLOK_SIZE 512           /* bytes per UCSD logical block */ 
#define SECT_PER_BLOK 4         /* physical sectors per logical block */ 
 
 
 
 
/*      : : : : : : : : : : : : : : : : : : : : : : : : : : : : 
 
        Globals -- these would be static if it were available 
 
        : : : : : : : : : : : : : : : : : : : : : : : : : : : : */ 
 
int lsn;                /* logical sector number */ 
int lstlsn;             /* last logical sector number */ 
int nbytes;             /* number of bytes remaining in ucsd file buffer */ 
char ucsdbuf[BLOK_SIZE]; /* 1 block buffer for ucsd file */ 
char *ptr;              /* pointer to current byte in ucsd file buffer */ 
char tofile[BUFSIZ];	/* cp/m destination file buffer */ 
 
 
 
 
 
/*     ------------------------------------------------------- 
 
        Name:           main(argc,argv) 
        Result:         --- 
        Errors:         invocation syntax 
        Globals:        lsn,lstlsn,nbytes,ptr,ucsdbuf 
        Macros:         CPM_FILE,UCSD_FILE,UCSD_NAM_SZ 
 
        Procedures:     puts(),exit(),force_upr(),printf() 
                        putchar(),close(),unlink(),strcpy() 
                        strcat(),getchar(),open(),copy() 
                        tolower() 
 
        Action:         Call copy() to copy a '.TEXT' file 
                        from a UCSD formatted disk in drive B 
                        to a user specified file name in 
                        drive A 
 
                        Handle invocation errors 
                        Handle case of already existing 
                        destination  file 
 
        ------------------------------------------------------- */ 
 
#define CPM_FILE argv[1] 
#define UCSD_FILE argv[2] 
 
main(argc,argv) 
        int argc; 
        char *argv[]; 
        { 
        int file_id; 
        char ucsdname[UCSD_NAM_SZ]; 
 
        lsn = lstlsn = nbytes = 0;      /* init globals */ 
        ptr = ucsdbuf; 
 
        if( argc != 3 ) { 
          puts("Proper invocation form is:\n\n"); 
          puts("UCSD2CPM <cpm-file-name> <ucsd-file-name>\n"); 
          puts("This copies B:<ucsd-file-name>.TEXT to A:<cpm-file-name>\n"); 
          exit(1); 
          } 
        force_upr(UCSD_FILE);   /* make <ucsd-file-name> upper case */ 
	if (fopen(CPM_FILE,tofile) >= 0 ) {
          printf("%s already exists. Delete it ? ",CPM_FILE); 
          if( tolower(getchar()) != 'y' ) { 
            puts("\naborted\n"); 
            exit(1); 
            } 
          putchar('\n'); 
          fclose(tofile); 
          unlink(CPM_FILE); 
          } 
        else 
          fclose(tofile); 
        strcpy(ucsdname,UCSD_FILE); 
        strcat(ucsdname,".TEXT"); 
        if(copy(ucsdname,CPM_FILE)) 
          printf("\n\nno such file %s .. aborted\n",ucsdname); 
        exit(0); 
        } 
 
/*     ------------------------------------------------------- 
 
        Name:           copy (ucsd_file,cpm_file) 
        Result:         TRUE if error , FALSE if transfer ok 
 
        Errors:         no such '.TEXT' file 
 
        Globals:        struct buf, 
        Macros:         TRUE,EOF_F,CR,DLE,NULL,CPM_EOF,FALSE 
 
        Procedures:     find_file(),fcreat(),getbyte() 
                        putc(),fflush(),fclose() 
 
        Action:         Find '.TEXT' file in UCSD directory 
                        on drive B 
                        Create user specified file on 
                        drive A 
                        copy file content from UCSD to CP/M 
                        car by char translating: 
                                Skip header block 
                                Ignore NULL's 
                                Add LF after CR 
                                Convert DLE <count> to 
                                appropriate number of spaces 
                        close output file 
 
        ------------------------------------------------------- */ 
 
copy(ucsd_file,cpm_file) 
        char *ucsd_file,*cpm_file; 
        { 

        int c; 
 
        if( ! find_file(ucsd_file) )            /* no such file */ 
          return TRUE; 
        if (fcreat(cpm_file,tofile) == ERROR) {
	  printf("Can't create %s, aborting..\n",cpm_file);
	  exit();
	}
        while( (c = getbyte()) != EOF_F ) 
          switch(c) { 
 
            case CR:    putc(CR,tofile);       /* LF after CR */ 
                        putc(LF,tofile); 
                        break; 
 
            case DLE:   for( c = (getbyte() - 32); c; c-- ) 
                          putc(' ',tofile);    /* fill spaces */ 
                        break; 
 
            case '\1':                          /* ignore ^A's */ 
            case NULL:  break;                  /* ignore nulls */ 
 
            default:    putc(c,tofile);        /* pass char unmodified */ 
                        break; 
            } 
        putc(CPM_EOF,tofile);                  /* send cp/m ascii endfile */ 
        fflush(tofile); 
        fclose(tofile); 
        return FALSE;                           /* signal transfer ok */ 
        } 
 
/*     ------------------------------------------------------- 
 
        Name:           getbyte() 
        Result:         next sequential byte from UCSD '.TEXT' file 
        Errors: 
        Globals:        lsn,lstlsn,nbytes,ptr,ucsdbuf[] 
        Macros:         EOF_F,BLOK_SIZE,SECT_PER_BLOK 
        Procedures:     read_ucsd() 
 
        Action:         read block at a time 
                        pass along byte at a time 
                        return EOF_F if end of UCSD file 
 
        ------------------------------------------------------- */ 
 
 
getbyte() 
        { 
        if( lsn > lstlsn) 
          return EOF_F; 
        if( nbytes-- ) 
          return *ptr++; 
        read_ucsd(ucsdbuf,lsn,1); 
        nbytes = (BLOK_SIZE-1); 
        ptr = ucsdbuf; 
        lsn += SECT_PER_BLOK; 
        return *ptr++; 
        } 
 
 
/*     ------------------------------------------------------- 
 
        Name:           find_file(ucsd_name) 
        Result:         TRUE if found, FALSE if not found 
        Errors: 
        Globals:        lsn,lstlsn 
        Macros:         D_ENT_SZ,SECT_PER_BLOK,D_TITLE 
                        TRUE,FALSE,UCSD_DIR_SZ 
 
        Procedures:     read_ucsd() 
 
        Action:         --- 
 
        ------------------------------------------------------- */ 
 
 
find_file(ucsd_name) 
        char *ucsd_name; 
        { 
        char ucsd_dir[UCSD_DIR_SZ]; 
        char *dir,*saved_dir,*name; 
        int name_len; 
 
        name = ucsd_name; 
        read_ucsd(ucsd_dir,(2*SECT_PER_BLOK),4); 
        dir = &ucsd_dir[D_TITLE + D_ENT_SZ];    /* skip title block */ 
        while ( dir < &ucsd_dir[UCSD_DIR_SZ] ) { 
          saved_dir = dir; 
          if( (name_len = *dir++) <=0 || name_len > 19 ) 
            return FALSE; 
          while( *dir++ == *name++ ) { 
            if( --name_len )    /* continue comparison check */ 
              ; 
            else {                              /* entry found */ 
              dir = saved_dir - D_TITLE;        /* point to alloc. info. */ 
              lsn = *dir++; 
              lsn += *dir++ << 8;   /* lsn = starting logical block number */ 
              lsn += 2;             /* bypass header blocks */ 
              lsn *= SECT_PER_BLOK; /* 1'st logical sector number */ 
 
              lstlsn = *dir++; 
              lstlsn += *dir++ << 8;    /* lstlsn = ending logical block # */ 
              lstlsn *= SECT_PER_BLOK;  /* lstlsn = last logical sector # */ 
              return TRUE;              /* indicate file found */ 
              } 
            } 
          name = ucsd_name;             /* reset comparison name pointer */ 
          dir = saved_dir + D_ENT_SZ;   /* point at next entry name */ 
          } 
        return FALSE;                           /* no such file */ 
        } 
 
/*     ------------------------------------------------------- 
 
        Name:           read_ucsd() 
        Result:         status of selecting drive A 
        Errors:         sector read error 
        Globals:        --- 
        Macros:         SEL_DSK,SET_DMA,SET_TRK,SET_SEC 
                        READ,DUMMY,SECT_SIZE 
        Procedures:     bios(),bdos(),printf(),exit() 
 
        Action:         Read count blocks from drive B 
                        into buf starting at UCSD logical 
                        record number rn 
 
        ------------------------------------------------------- */ 
 
 
read_ucsd(buf,rn,count) 
        char *buf; 
        unsigned rn; 
        int count; 
        { 
        char bios(); 
        int seccnt; seccnt = count*4; 
 
        bdos(SEL_DSK,1); 
        while( seccnt-- ) { 
          bdos(SET_DMA,buf); 
          bios(SET_TRK,track(rn)); 
          bios(SET_SEC,sector(rn)); 
          if( bios(READ,DUMMY) ) { 
            printf("read error @ track %2d sector %2d",track(rn),sector(rn)); 
            exit(1); 
            } 
          buf += SECT_SIZE; 
          rn++; 
          } 
        return bdos(SEL_DSK,0); 
        } 
 
/*     ------------------------------------------------------- 
 
        Name:           sector(rn) 
        Result:         absolute sector number 
        Errors:         --- 
        Globals:        --- 
        Macros:         --- 
        Procedures:     --- 
 
        Action:         convert logical record number 
                        to absolute sector 
 
                        maps logical records to physical sectors 
                        by selecting every second sector in order 
                        (accounting for the modulo 26 process) 
                        on the diskette except that at a track 
                        switchover point there is an additional 
                        'gap' of 6 sectors (total of 7) to allow 
                        for the drive to seek. This is UCSD's 
                        attempt to minimize disk access time. 
 
        ------------------------------------------------------- */ 
 
sector(rn) 
        unsigned rn; 
        { 
        unsigned t1,t2,trk,t3,sect; 
 
        t1 = rn % 26; 
        t2 = t1 << 1; 
        if(t1 > 12) 
          t2++; 
        trk = rn/26;            /* zero based absolute track */ 
        t3 = t2 + 6*trk;        /* new logical sector number */ 
        sect = t3 % 26;         /* new zero based absolute sector */ 
        return ++sect;          /* one based absolute sector */ 
        } 
 
/*      ------------------------------------------------------- 
 
        Name:           track(rn) 
        Result:         absolute track number 
        Errors:         --- 
        Globals:        --- 
        Macros:         --- 
        Procedures:     --- 
 
        Action:         convert logical sector number to 
                        absolute track This is simply the modulo 
                        26 process except that track 0 is not 
                        considered part of the logical 
                        sector space. 
 
        ------------------------------------------------------- */ 
 
 
track(rn) 
        unsigned rn; 
        { 
        return rn/26 + 1; 
        } 
 
/*     ------------------------------------------------------- 
 
        Name:           index(str,sstr) 
        Result:         position of string sstr in string str 
                        -1 if not a substring 
        Errors:         --- 
        Globals:        --- 
        Macros:         --- 
        Procedures:     --- 
 
        Action:         --- 
 
        ------------------------------------------------------- */ 
 
 
index(str,sstr) 
        char *str,*sstr; 
        { 
        int first_match; 
 
        if( *sstr == 0 )  /* null string is a substring of all strings */ 
          return 0; 
        for( first_match=0; *str != *sstr; first_match++) 
          if( *str == 0 ) 
            return -1; 
          else 
            str++; 
        while( *sstr ) 
          if( *str++ != *sstr++ ) 
            return -1; 
        return first_match; 
        } 
 
/*      ------------------------------------------------------- 
 
        Name:           force_upr(string) 
        Result:         --- 
        Errors:         --- 
        Globals:        --- 
        Macros:         --- 
        Procedures:     --- 
 
        Action:         force each char of string to upper case 
 
        ------------------------------------------------------- */ 
 
force_upr(string) 
        char *string; 
        { 
        while(*string) 
          *string = *string++; 
        return; 
        } 
 
/*      ------------------------------------------------------- 
 
        Name:           print(string,n) 
        Result:         --- 
        Errors:         --- 
        Globals:        --- 
        Macros:         --- 
        Procedures:     putchar() 
 
        Action:         print n chars to console starting 
                        at char pointer string 
 
        ------------------------------------------------------- */ 
 
print(string,n) 
        char *string; 
        int n; 
        { 
        while(n--) 
          putchar(*string++); 
        return; 
        } 

