home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / sigm / vol128 / umodem.lbr / CPMUTL.C next >
Encoding:
C/C++ Source or Header  |  1985-02-10  |  32.4 KB  |  1,040 lines

  1. /* copyright (c) by Aaron Wohl, 1981,1982
  2.   written by Aaron Wohl 12-24-81 (wohl@cmuc)
  3.   This file may be used for non-profit use provided this
  4.     this notice remains at the front of the file.
  5.  
  6.   This program reads and writes cpm format floppy disks.
  7.   cpm is a trade mark of Digital Reasearch.  It runs under
  8.   version 7 unix.  unix is a trademark of bell labs.
  9.   See the function help() for documentation.
  10.  
  11.   please mail any bug fixes to wohl@cmuc
  12.   the source for this file is on the unix host vlsi@cmuc
  13.     pathname /usr/avw/cpmutl/cpmutlVERSION-NUMBER.c
  14.     vlsi is on the arpanet running TCP-IP
  15.   a copy is also kept on mit-mc cpm;ar43:cpmutl VERSION-NUMBERc
  16.   and on [cmuc]ps:<wohl.bar>cpmutl.c.VERSION-NUMBER (for people that can't
  17.     figure out how to use ITS)
  18.   mail will be sent info-cpm announcing new versions
  19.   
  20. */
  21.  
  22. #define version 7
  23. #define when "10-28-82"
  24.  
  25. /* change log:
  26.  
  27.  ver   when          who                why
  28.  ---  --------  -------------   --------------------
  29.    2  12-27-81        wohl@cmuc   allow a trailing * to cross the . in a ufn
  30.    3  12-28-81        wohl@cmuc   remove refrences to cmu local functions
  31.                 honor the record count when reading
  32.    4   2- 9-82      wohl@cmuc   fix a '=> [cpm]' string to be '[cpm] =>'
  33.                 put with one filename arg uses it for both
  34.    5   5- 8-82        wohl@cmuc   initilize extent filler bytes to zero
  35.    6   5-17-82      wohl@cmuc   fix printout of free space (was 2k low)
  36.                 add support for user numbers
  37.                 add a \n to the interleving off message
  38.    7  10-28-82      mz@gp       for file get, close each file
  39.  
  40. Things to (think about) doing:
  41.  
  42. a) have the create file system function ask for confirmation
  43. b) multiple put should skip files with names that are too long or truncate the
  44.    name
  45. c) skip files which are directories (multiple put)
  46.  
  47.  
  48. */
  49.  
  50. #include <sys/types.h>
  51. #include <sys/stat.h>
  52. #include <stdio.h>
  53. #include <ctype.h>
  54.  
  55. #define TRUE 1
  56. #define FALSE 0
  57. #define MAX_USER 15        /* maximum user number */
  58. #define CP_NUMDIR 64        /* number of directory slots */
  59. #define DIR_EXT (CP_NUMDIR+1)    /* fake index to allocate directory space */
  60. #define CP_DIRSEC 16        /* number of sectors of directory */
  61. #define CP_NAMESIZE (8+3+1+1)    /* file name, name+ext+dot+null */
  62. #define CP_BASE    (26*2)        /* sector of start of file system */
  63. #define CP_SIZE    (26*77)        /* sectors on a disk */
  64. #define CP_CLUSTER 8        /* sectors in a allocation cluster */
  65. #define CP_CLCHAR (CP_CLUSTER*128) /* number of bytes in a cluster */
  66. #define CP_ALOC ((CP_SIZE-CP_BASE)/CP_CLUSTER) /* clusters per disk */
  67. #define SPACE 040        /* ascii space character */
  68. #define np(val) ((val) & 0177)    /* no parity please */
  69. #define FT_SZ 3            /* number of bytes in a file type */
  70. #define FN_SZ 8            /* number of bytes in a file name */
  71. #define SEC 128            /* bytes in a sector */
  72. #define DM 16            /* number of disk map entries */
  73. #define DELF 0xe5        /* entry type for a deleted file */
  74.  
  75. /* what a directory entry looks like */
  76. struct cp_dir_ext {
  77.   unsigned char cp_et;        /* entry type */
  78.   unsigned char cp_fn[FN_SZ];    /* file name */
  79.   unsigned char cp_ft[FT_SZ];    /* file type */
  80.   unsigned char cp_ex;        /* extent number */
  81.   unsigned char cp_fl[2];    /* filler */
  82.   unsigned char cp_rc;        /* record count */
  83.   unsigned char cp_dm[DM];    /* disk allocation map */
  84. };
  85.  
  86. struct cp_dir_ext cp_dir[CP_NUMDIR];
  87.  
  88. char cp_name[CP_NUMDIR][CP_NAMESIZE]; /* name as file.typ */
  89. int ext_next[CP_NUMDIR];    /* slot of next higher extent */
  90. int ext_prev[CP_NUMDIR];    /* slot of next lower extent */
  91. int cp_btb[CP_ALOC];        /* allocation bit table */
  92. int dsk_inuse;            /* number of clusters in use */
  93. int num_fil;            /* number of files */
  94. int num_ext;            /* number of extents */
  95. int dir_err;            /* number of directory errors */
  96.                 /*  includes number of i/o errors in dir */
  97. int io_err;            /* number of i/o errors */
  98. int user_num = 0;        /* current user number */
  99. int any_user = TRUE;        /* true if files from any user can be seen */
  100.  
  101. #define flag(c) (flg[(c) - 'a'])
  102.  
  103. char *man = { "clgp" };
  104.  
  105. char *flname;
  106. int fldes;
  107. long lseek();
  108.  
  109. int gcmd(),lcmd(),icmd(),ccmd(),pcmd(),ncmd(); /* command functions */
  110.  
  111. int (*comfun)();
  112. char flg[26];
  113. char mode = 'u';
  114. int file;
  115.  
  116. /* main program */
  117. main(argc, argv)
  118. char *argv[];
  119. { char *cp;
  120.   fprintf(stdout,"cpmutl version(%d), date:%s\n",version,when);
  121.   init_data();            /* initialize data */
  122.   cp = argv[1];
  123.   for(cp = argv[1]; *cp; cp++)
  124.     switch(*cp) {
  125.     case 'a':
  126.       flag('a')++;
  127.       continue;     
  128.  
  129.     case 't':
  130.       mode = 't';
  131.       continue;
  132.  
  133.     case 'b':
  134.       mode = 'b';
  135.       continue;
  136.  
  137.     case 'o':
  138.       flag('o')++;        /* allow writes to a damaged directory */
  139.       continue;
  140.  
  141.     case 'i':
  142.       flag('i')++;
  143.       continue;
  144.  
  145.     case 'n':
  146.       flag('w')++;        /* will be writing */
  147.       setcom(ncmd);
  148.       continue;
  149.  
  150.     case 'p':
  151.       flag('w')++;        /* will be writing */
  152.       setcom(pcmd);
  153.       continue;
  154.  
  155.     case 'c':
  156.       flag('w')++;        /* will be writing */
  157.       setcom(ccmd);
  158.       continue;
  159.  
  160.     case 'g':
  161.       setcom(gcmd);
  162.       continue;
  163.  
  164.     case 'l':
  165.       setcom(lcmd);
  166.       continue;
  167.  
  168.     case 'f':
  169.       flname = argv[2];
  170.       argv++;
  171.       argc--;
  172.       continue;
  173.  
  174.     case 'h':
  175.       help();
  176.  
  177.     case 'u':
  178.       user_num=0;        /* read in the number */
  179.       while(isdigit(*(cp+1)))
  180.         user_num = user_num * 10 + (*++cp - '0');
  181.       if(user_num > MAX_USER)
  182.         quit(1,"\ncpmutl:user number out of range\n");
  183.       any_user = FALSE;
  184.       continue;
  185.  
  186.     default:
  187.       quit(1,"\ncpmutl: bad option '%c'. use h for help\n", *cp);
  188.     }
  189.   if(comfun == 0) {
  190.       quit(1,"\ncpmutl: one of [%s] must be specified. use h for help\n",man);
  191.     }
  192.   (*comfun)(argc-2,&argv[2]);
  193.   fprintf(stdout,"\n");
  194.   exit(0);
  195. }
  196.  
  197. /* command parsing subroutines */
  198.  
  199. need_mode()
  200. { if(mode == 'u')
  201.     quit(1,"\ncpmutl: mode t(ext) or b(inary) must be specified\n");
  202. }
  203.  
  204. setcom(fun)
  205. int (*fun)();
  206. { if(comfun != 0)
  207.     quit(1,"\ncpmutl: only one of [%s] allowed\n", man);
  208.   comfun = fun;
  209. }
  210.  
  211. help()
  212. { fprintf(stdout,"commands:\n");
  213.   fprintf(stdout," l afn                  list  files matching afn\n");
  214.   fprintf(stdout," g afn [unix_prefix]    get files matching afn\n");
  215.   fprintf(stdout," p ufn unix_file_name   put a file to floppy\n");
  216.   fprintf(stdout," p '*' unix_files       put a bunch of files\n");
  217.   fprintf(stdout," n afn                  nuke - delete matching files\n");
  218.   fprintf(stdout," c                      create a new file system\n");
  219.   fprintf(stdout,"\noptions:\n");
  220.   fprintf(stdout," t - text mode             b - binary mode\n");
  221.   fprintf(stdout," i - don't do interleaving u - user number\n");
  222.   fprintf(stdout," a - absolute disk address, start.length e.g. 0.2002\n");
  223.   fprintf(stdout," f - use the named unix file instead of /dev/floppy\n");
  224.   fprintf(stdout," o - override, allows writes to a damaged directories\n");
  225.   fprintf(stdout,"\nterms:\n");
  226.   fprintf(stdout," afn - ambiguous cpm file name, e.g. *.asm or foo*\n");
  227.   fprintf(stdout,"   *** don't forget to quote afns to the shell, '*.asm'\n");
  228.   fprintf(stdout," ufn - unambiguous file name\n");
  229.   fprintf(stdout," interleaving - tracks 2-76 are normaly interleaved\n");
  230.   fprintf(stdout,"   with a skew of 6 sectors\n");
  231.   exit(1);
  232. }
  233.  
  234. /* create a new file system */
  235. ccmd()
  236. { int i;
  237.   fprintf(stdout,"initializing file system ");
  238.   fflush(stdout);
  239.   lread(CP_BASE,CP_DIRSEC,cp_dir);    /* read in the directory */
  240.                     /* so we can zap disk to undo this */
  241.   for(i=0; i<CP_NUMDIR; i++)
  242.     cp_dir[i].cp_et = DELF;        /* set all the extents deleted */
  243.   lwrite(CP_BASE,CP_DIRSEC,cp_dir);    /* write it back */
  244.   fprintf(stdout," [ok]\n");
  245.  
  246. /* get some files from the floppy */
  247. gcmd(argc,argv)
  248.  char *argv[];
  249.  int argc;
  250. { char *unix_name,*cpm_name;
  251.   int index,cur;
  252.   struct cp_dir_ext wild;
  253.   cpm_name = argv[0];
  254.   if (argc > 2) toomany();
  255.   if(flag('a')) {            /* want absolute disk addresses? */
  256.     if(argc < 2) toofew();
  257.     abs_read(cpm_name,argv[1]);
  258.     return;
  259.     }
  260.  
  261.   need_mode();                /* need have given a file mode */
  262.   if (argc == 1)
  263.     unix_name = "";            /* no unix prefix */
  264.   else
  265.     unix_name = argv[1];        /* use this unix prefix */
  266.   parse_ext(&wild,cpm_name);
  267.   getdir();                /* read the directory */
  268.   cur = -1;                /* start looking here */
  269.   while((cur = lookup(&wild,cur)) != -1) /* find the next matching file */
  270.     getfile(cur,unix_name);
  271. }
  272.  
  273. /* pcmd - put files on the floppy */
  274. pcmd(argc,argv)
  275.  char *argv[];
  276.  int argc;
  277. { int i;
  278.   char *uname;
  279.   if(argc<1) toofew();
  280.   if(flag('a')) {            /* want absolute disk addresses? */
  281.     if(argc<2) toofew();
  282.     if(argc > 2) toomany();
  283.     abs_write(argv[0],argv[1]);
  284.     return;
  285.     }
  286.  
  287.   need_mode();
  288.   getdir();
  289.   if (argc == 1)                /* only one arg? */
  290.     writefile(argv[0],argv[0]);
  291.   else
  292.     for(i=1; i<argc; i++)        /* write out the unix files */
  293.       writefile(argv[0],argv[i]);
  294. }
  295.  
  296. /* write a bunch of sectors into a file */
  297. abs_write(whereto,unix_name)
  298. { int start,size,status;
  299.   char buf[SEC];
  300.   FILE *ifile;
  301.   if(sscanf(whereto,"%d.%d",&start,&size) != 2)
  302.     quit(1,"\ncpmutl:can't parse start.size address\n");
  303.   if ((ifile = fopen(unix_name,"r")) == NULL) /* try to open the unix file */
  304.     quit(1,"\ncpmutl:can't open unix file %s for read\n",unix_name);
  305.   markerrs();
  306.   fprintf(stdout,"writing %s => sector %d, length %d ",unix_name,start,size);
  307.   fflush(stdout);
  308.   while(size--) {
  309.     if((status = fread(buf,1,SEC,ifile)) != SEC) /* read a sector from unix */
  310.       quit(1,"\ncpmutl:read error on unix file %s\n",unix_name);
  311.     lwrite(start,1,buf);
  312.     start++;                /* advance to the next sector */
  313.     }
  314.  reporterrs();
  315.  fclose(ifile);
  316. }
  317.  
  318. /* ndcm - nuke some files */
  319. ncmd(argc,argv)
  320. char *argv[];
  321. int argc;
  322. { struct cp_dir_ext del_ext;
  323.   if(argc < 1) toofew();
  324.   if(argc > 2) toomany();
  325.   getdir();                /* get the directory */
  326.   parse_ext(&del_ext,argv[0]);        /* parse the file spec to delete */
  327.   nuke_ext(&del_ext);            /* delete matching extents */
  328.   putdir();
  329. }
  330.  
  331. /* delete matching entries */
  332. nuke_ext(nuk_me)
  333. struct cp_dir_ext *nuk_me;
  334. { int i;
  335.   for(i=0; i<CP_NUMDIR; i++)
  336.     if((cp_dir[i].cp_et != DELF) &&
  337.        match_ext(nuk_me,&cp_dir[i]))    /* does this extent match? */
  338.       flush_ext(i);            /* yes flush it */
  339. }
  340.  
  341. /* flush this extent */
  342. flush_ext(index)
  343. { int i,where;
  344.   num_ext--;                /* one less extent exists */
  345.   if(cp_dir[index].cp_ex == 0)
  346.     fprintf(stdout,"deleting file:[%d]%s\n",
  347.       cp_dir[index].cp_et,cp_name[index]),
  348.     num_fil--;                /* one less file */
  349.   for(i=0; i<DM; i++)            /* release its disk space */
  350.     if((where = cp_dir[index].cp_dm[i]) != 0) { /* this cluster allocated? */
  351.       if(cp_btb[where] != index)    /* we did have it didn't we? */
  352.        dir_err++,            /* no, an error then */
  353.        fprintf(stderr,"\ncpmutl:cluster being deleted is not assigned\n"); 
  354.       cp_btb[where] = -1;
  355.       dsk_inuse--;
  356.       }
  357.   ext_prev[index] = -1;            /* no links this way */
  358.   ext_next[index] = -1;            /* or that */
  359.   cp_dir[index].cp_et = DELF;        /* do the deed */
  360. }
  361.  
  362. /* copy the name of an extent */
  363. copy_ext(src,dst)
  364. struct cp_dir_ext *src,*dst;
  365. { int i;
  366.   for(i=0; i<FN_SZ; i++)        /* copy over the file name */
  367.     dst->cp_fn[i] = src->cp_fn[i];
  368.   for(i=0; i<FT_SZ; i++)        /* copy over the file type */
  369.     dst->cp_ft[i] = src->cp_ft[i];
  370. }
  371.  
  372. /* lcmd - list the floppy directory */
  373. lcmd(argc,argv)
  374. char *argv[];
  375. int argc;
  376. { int i,cur,curext;
  377.   int totrec=0,totext=0,totk=0;
  378.   struct cp_dir_ext wild;
  379.   char *cpm_name;
  380.   if(argc > 1) toomany();
  381.   if(argc == 0)
  382.     cpm_name = "*.*";        /* default file name */
  383.   else
  384.     cpm_name = argv[0];
  385.   getdir();            /* read in the directory */
  386.   fprintf(stdout,
  387.     "disk status: %d files, %d extents, %dK allocated, %dK free\n\n",
  388.     num_fil,num_ext,dsk_inuse-2,CP_ALOC-dsk_inuse);
  389.   fprintf(stdout,"    recs     K   ex   usr name\n");
  390.  
  391.   parse_ext(&wild,cpm_name);    /* parse the cpm file name */
  392.   cur = -1;
  393.   while((cur = curext = lookup(&wild,cur)) != -1) { /* find a file */
  394.     int recsiz=0,ksiz=0,numext=0;
  395.     while(curext != -1) {        /* do all of its extents */
  396.       numext++;                /* found one more extent */
  397.       recsiz += cp_dir[curext].cp_rc;    /* this many records */
  398.       ksiz += ((cp_dir[curext].cp_rc+CP_CLUSTER-1)/CP_CLUSTER); /* k size */
  399.       curext = ext_next[curext];    /* move on */
  400.       }
  401.     fprintf(stdout,
  402.       "    %4d   %3dK  %2d   [%d]%s\n",recsiz,ksiz,numext,
  403.       cp_dir[cur].cp_et,cp_name[cur]);
  404.     totrec += recsiz;
  405.     totk += ksiz;
  406.     totext += numext;
  407.     }
  408.   fprintf(stdout,"    ----   ---   --\n");
  409.   fprintf(stdout,"    %4d   %3d   %2d\n",totrec,totk,totext);
  410. }
  411.  
  412. /*
  413.    read subroutines
  414. */
  415.  
  416. /* read a file from the floppy */
  417. getfile(index,unix_prefix)
  418. int index;
  419. char *unix_prefix;
  420. { int clstr,cur_io_err,extsiz,currec;
  421.   FILE *ofile;
  422.   char *unix_name;
  423.   char buf[100];            /* default to the prefix */
  424.   if (*unix_prefix == 0)        /* is there a unix file name */
  425.     unix_name = cp_name[index];        /* no, just use the cpm name */
  426.   else {
  427.     char *cp;
  428.     for(cp = unix_prefix; *cp; *cp++ );    /* find the end */
  429.     cp--;                /* point to the last char */
  430.     if (*cp != '/')            /* is this a prefix */
  431.       unix_name = unix_prefix;        /* no, its a name */
  432.     else
  433.       strcpy(buf,unix_prefix),        /* put on the prefix */
  434.       strcat(buf,cp_name[index]),    /* then the name */
  435.       unix_name = buf;            /* that's it */
  436.     }
  437.  
  438.   if ((ofile = fopen(unix_name,"w")) == NULL) /* try to open the unix file */
  439.     quit(1,"\ncpmutl:can't create unix file %s\n",unix_name);
  440.   fprintf(stdout,"[%d]%s => %s ",cp_dir[index].cp_et,
  441.                  cp_name[index],unix_name);
  442.   fflush(stdout);
  443.   markerrs();
  444.   for( ; index != -1; index=ext_next[index]) { /* all extents */
  445.     extsiz = cp_dir[index].cp_rc;    /* read all the records */
  446.     for(currec=0; currec < extsiz; currec++) { /* read all the records */
  447.      if(read_sector(cp_dir[index].cp_dm[currec/CP_CLUSTER],
  448.                     currec % CP_CLUSTER,ofile))
  449.        break;                /* stop at eof */
  450.      }
  451.     }
  452.   reporterrs();
  453.   fclose(ofile);
  454. }
  455.  
  456. /* more read subroutines */
  457.  
  458. /* read a bunch of sectors into a file */
  459. abs_read(wherefrom,unix_name)
  460. { int start,size,status;
  461.   char buf[SEC];
  462.   FILE *ofile;
  463.   if(sscanf(wherefrom,"%d.%d",&start,&size) != 2)
  464.     quit(1,"\ncpmutl:can't parse start.size address\n");
  465.   if ((ofile = fopen(unix_name,"w")) == NULL) /* try to open the unix file */
  466.     quit(1,"\ncpmutl:can't create unix file %s\n",unix_name);
  467.   markerrs();
  468.   fprintf(stdout,
  469.     "reading from sector %d, length %d => %s ",start,size,unix_name);
  470.   fflush(stdout);
  471.   while(size--) {
  472.     lread(start,1,buf);            /* read a sector from the floppy */
  473.     status=fwrite(buf,1,SEC,ofile);    /* write it to the file */
  474.     if(status != SEC)
  475.       quit(1,"\ncpmutl:file write error for unix file %s\n",unix_name);
  476.     start++;                /* advance to the next sector */
  477.     }
  478.   reporterrs();
  479.   fclose(ofile);
  480. }
  481.  
  482. /* read in a sector */
  483. int read_sector(dsk_adr,sec,ofile)
  484. int dsk_adr,sec;
  485. FILE *ofile;
  486. { int i;
  487.   char buf[SEC],cur;            /* sector buffer */
  488.   lread((dsk_adr*CP_CLUSTER)+CP_BASE+sec,1,buf); /* read it in */
  489.   if(sec == 0)                /* start of a cluster? */
  490.     putc('.',stdout),            /* yes */
  491.     fflush(stdout);
  492.   if (mode == 't') {
  493.     for(i=0; i<SEC; i++)        /* do text translation */
  494.       switch(cur=np(buf[i])) {
  495.       case 'Z'-64:
  496.         return TRUE;            /* stop at eof */
  497.       case 'M'-64:
  498.         continue;            /* throw away returns */
  499.       default:
  500.     if(fwrite((char *)&cur,1,1,ofile) != 1)
  501.           quit(1,"\ncpmutl:write error on unix file\n");
  502.       }
  503.     }
  504.   else if(fwrite(buf,SEC,1,ofile) != 1)
  505.     quit(1,"\ncpmutl:write error on unix file\n");
  506.  return FALSE;
  507. }
  508.  
  509. /* extent handling */
  510.  
  511. /* see if ext1 matches ext2 */
  512. /* the user number field of ext is checked against user_num */
  513. match_ext(ext1,ext2)
  514. struct cp_dir_ext *ext1,*ext2;
  515. { int i;
  516.   if(!any_user && ext2->cp_et != user_num)
  517.     return FALSE;        /* fail if not the correct user */
  518.   for(i=0; i<FN_SZ; i++)
  519.     if((np(ext1->cp_fn[i]) != np(ext2->cp_fn[i])) && /* fail if not equal */
  520.        (np(ext1->cp_fn[i]) != '?'))        /* and ext1 not wild */
  521.       return FALSE;
  522.   for(i=0; i<FT_SZ; i++)
  523.     if((np(ext1->cp_ft[i]) != np(ext2->cp_ft[i])) && /* fail if not equal */
  524.        (np(ext1->cp_ft[i]) != '?'))        /* and ext1 not wild */
  525.       return FALSE;
  526.   return TRUE;                    /* success */
  527. }
  528.  
  529. /* parse the passed extent */
  530. parse_ext(rslt,name)
  531. struct cp_dir_ext *rslt;
  532. char *name;
  533. { int i; char cur;
  534.   ini_ext(rslt);            /* initialize the extent */
  535.   for(i=0; i<FN_SZ; i++) {
  536.     cur = *name;            /* fetch the char */
  537.     if(islower(cur))
  538.       cur = toupper(cur);        /* convert to upper case */
  539.     if((cur == 0) || (cur == '.'))    /* stop at eos */
  540.       break;
  541.     else if(cur == '*')            /* is it a star? */
  542.       rslt->cp_fn[i] = '?';        /* fill out with ? then */
  543.     else
  544.       rslt->cp_fn[i] = cur,
  545.       name++;
  546.     }
  547.  
  548.   if(*name == '*') {            /* need to skip a star? */
  549.     *name++;                /* get out of the * */
  550.     if(*name == 0)            /* the end of the string? */
  551.      name = "*";            /* yes, start crosses the . then */
  552.     }
  553.   if(*name == '.') *name++;        /* maybe skip the separator */
  554.  
  555.   for(i=0; i<FT_SZ; i++) {
  556.     cur = *name;            /* fetch the char */
  557.     if(islower(cur))
  558.       cur = toupper(cur);        /* convert to upper case */
  559.     if(cur == 0)            /* stop at eos */
  560.       break;
  561.     else if(cur == '*')            /* is it a star? */
  562.       rslt->cp_ft[i] = '?';        /* fill out with ? then */
  563.     else
  564.       rslt->cp_ft[i] = cur,
  565.       name++;
  566.     }
  567.   if(*name == '*') *name++;        /* get out of the * */
  568.   if(*name != 0)
  569.     quit(1,"\ncpmutl:cpm file name to long\n");
  570. }
  571.  
  572. ini_ext(rslt)
  573. struct cp_dir_ext *rslt;
  574. { int i;
  575.   rslt->cp_et = user_num;    /* mark this extent in use */
  576.   for(i=0; i<FN_SZ; i++)    /* set up the file name */
  577.     rslt->cp_fn[i] = SPACE;
  578.   for(i=0; i<FT_SZ; i++)    /* and the file type */
  579.     rslt->cp_ft[i] = SPACE;
  580.   rslt->cp_rc = 0;        /* no records yet */
  581.   rslt->cp_ex = 0;        /* relative extent number */
  582.   for(i=0; i<DM; i++)
  583.     rslt->cp_dm[i] = 0;        /* no disk space assigned yet */
  584.   for(i=0; i<2; i++)
  585.     rslt->cp_fl[i] = 0;        /* initilize filler words */
  586. }
  587.  
  588. /* random small subroutines */
  589.  
  590. /* record io errors for a transfer */
  591. int cur_io_err;
  592. markerrs()
  593. { cur_io_err = io_err;
  594. }
  595.  
  596. /* print [ok] or number of io errors */
  597. reporterrs()
  598. { if(cur_io_err != io_err)
  599.     fprintf(stderr,"\ncpmutl:%d io errors\n",io_err-cur_io_err);
  600.   else
  601.     fprintf(stdout," [ok]\n");
  602. }
  603.  
  604. /* find the next file to match cpm_name */
  605. lookup(wild,index)
  606. int index;
  607. struct cp_dir_ext *wild;
  608. { index++;                /* skip over the current extent */
  609.   for( ; index<CP_NUMDIR; index++) {
  610.     if ((cp_dir[index].cp_et == DELF) || /* forget if deleted */
  611.         (cp_dir[index].cp_ex != 0) ||    /*  or not extent 0 */ 
  612.         (!match_ext(wild,&cp_dir[index])))
  613.       continue;                /* forget it */
  614.     return(index);            /* found a match */
  615.     };
  616.   return -1;                /* no more matches */
  617. }
  618.  
  619. /* assert that the directory has not been found to be inconsistent */
  620. dirok()
  621. { if (dir_err == 0)            /* is the directory ok? */
  622.     return;
  623.   if(flag('o')) {                /* override */
  624.     fprintf(stderr,
  625.       "\ncpmutl:directory has %d error(s).  proceeding...\n",dir_err);
  626.     return;
  627.     }
  628.   quit(1,"\ncpmutl:command aborted due to %d directory error(s)\n",dir_err);
  629. }
  630.  
  631. /* some more random subroutines */
  632.  
  633. /* too many args */
  634. toomany()
  635. { quit(1,"\ncpmutl:too many arguments\n");
  636. }
  637.  
  638. /* too few args */
  639. toofew()
  640. { quit(1,"\ncpmutl:too few arguments");
  641. }
  642.  
  643. /* initialize data */
  644. init_data()
  645. { int i;
  646.   for(i=0; i<CP_ALOC; i++)
  647.     cp_btb[i] = -1;        /* initialize the bit table to free */
  648.   dsk_inuse=num_fil=num_ext=0;    /* random statistics counters */
  649.   io_err=dir_err=0;        /* error counts */
  650.   for(i=0; i<CP_NUMDIR; i++) {    /* set up the links */
  651.     ext_next[i]=ext_prev[i] = -1; /* no links */
  652.     cp_name[i][0]=0;        /*  or names yet */
  653.     }
  654.   flname = "/dev/floppy";     /* default floppy file */
  655. }
  656.  
  657. /* mark a bit in use in the bit table */
  658. set_btb(where,who)
  659. int where,who;
  660. { if((who != DIR_EXT) &&    /* really part of the directory? */
  661.      (where == 0))        /* is it really allocated? */
  662.     return;            /* no, just a place holder */
  663.   if (where >= CP_ALOC) {    /* does this cluster exist? */
  664.     dir_err++;
  665.     fprintf(stderr,"\ncpmutl:illegal cluster %d in disk map for ",where);
  666.     prname(who);
  667.     fprintf(stderr,"\n");
  668.     }
  669.   else if (cp_btb[where] != -1) { /* has someone else already claimed it? */
  670.     dir_err++;
  671.     fprintf(stderr,"\ncpmutl:Cluster %d multiply linked:\n",where);
  672.     fprintf(stderr,"  first file: "); prname(who);
  673.     fprintf(stderr,"  second file: "); prname(cp_btb[where]);
  674.     }
  675.   else {
  676.     cp_btb[where] = who;    /* remember who has it for debugging */
  677.     dsk_inuse++;        /* one more cluster in use */
  678.     }
  679. }
  680.  
  681. /* print the name of a file */
  682. prname(index)
  683. int index;
  684. { int i,temp;
  685.   if(index == DIR_EXT)
  686.     fprintf(stderr,"directory\n");
  687.   else
  688.     fprintf(stderr,"name:[%d]%s, extent:%d, recs:%d, slot:%d\n",
  689.         cp_dir[index].cp_et,cp_name[index],cp_dir[index].cp_ex,
  690.         cp_dir[index].cp_rc,index);
  691. }
  692.  
  693. /* check that the record count for index is consistent with */
  694. /* the list of clusters allocated to it in the extent map */
  695. check_rc(index)
  696. int index;
  697. { int bit_aloc,j;
  698.   bit_aloc=(cp_dir[index].cp_rc+CP_CLUSTER-1)/CP_CLUSTER;
  699.   if(bit_aloc > DM) {            /* is it in range? */
  700.     dir_err++;                /* no */
  701.     fprintf(stderr,"\ncpmutl:record count out of bounds\n  for extent:");
  702.     prname(index);
  703.     return;                /* give up on this one */
  704.     }
  705.  
  706.   for(j=0; j<bit_aloc; j++)        /* there should be enough clusters */
  707.     if(cp_dir[index].cp_dm[j] == 0)    /* allocated to cover all records */
  708.       dir_err++,            /* else an error */
  709.       fprintf(stderr,
  710.         "\ncpmutl:allocation index %d should be allocated but isn't\n",j),
  711.       fprintf(stderr,"  for extent:"),
  712.       prname(j);
  713.  
  714.   for(j=bit_aloc; j<DM; j++)        /* the rest (if any) shouldn't be */
  715.     if(cp_dir[index].cp_dm[j] != 0)    /* allocated */
  716.       dir_err++,            /* else an error */
  717.       fprintf(stderr,
  718.         "\ncpmutl:allocation index %d shouldn't be allocated but is\n",j),
  719.       fprintf(stderr,"  for extent:"),
  720.       prname(j);
  721. }
  722.  
  723. /* link extra extents to there next lower extent */
  724. link_ext()
  725. { int index,j;
  726.   for(index=0; index<CP_NUMDIR; index++) { /* link to previous extents */
  727.     if ((cp_dir[index].cp_et == DELF) || /* if this extent is deleted */
  728.         (cp_dir[index].cp_ex == 0))    /*  or there is no lower extent */
  729.       continue;                /* then nothing to link to */
  730.     for(j=0; j<CP_NUMDIR; j++) {    /* find the lower extent */
  731.       if ((cp_dir[j].cp_ex != cp_dir[index].cp_ex-1) || /* wrong ext? */
  732.       (cp_dir[j].cp_et != cp_dir[index].cp_et) || /* or wrong user */
  733.           (strcmp(cp_name[j],cp_name[index]) != 0)) /*  or the wrong name */
  734.         continue;            /*  then keep looking */
  735.       ext_next[j] = index;        /* set the links */
  736.       ext_prev[index] = j;
  737.       if(cp_dir[j].cp_rc != SEC)    /* the lower extent does have the */
  738.         dir_err++,            /* correct record count doesn't it? */
  739.         fprintf(stderr,"\ncpmutl:incorrect record count: "),
  740.         prname(j);
  741.       break;
  742.       };
  743.     if (j >= CP_NUMDIR) {        /* was a lower extent found ? */
  744.       dir_err++;            /* no, that is an error */
  745.       fprintf(stderr,"\ncpmutl:can't find lower extent: "); prname(index);
  746.       }
  747.     }
  748. }
  749.  
  750. /* set cp_name from cp_fn and cp_ft */
  751. setname(index)
  752. { int i,j,temp;
  753.   j=0;                    /* index into the file name */    
  754.   for(i=0; i<FN_SZ; i++) {
  755.     temp = np(cp_dir[index].cp_fn[i]); /* no phoonly attributes */
  756.     if ((temp < SPACE) || temp == 0177)    /* is it printable? */
  757.       dir_err++,
  758.       fprintf(stderr,
  759.         "\ncpmutl:illegal char 0%o in file name, slot:%d\n",temp,index);
  760.     if (temp == SPACE)
  761.       break;                /* stop on a space */
  762.     cp_name[index][j++] = temp;
  763.     }
  764.   cp_name[index][j++] = '.';        /* put in the . */
  765.   for(i=0; i<FT_SZ; i++) {
  766.     temp = np(cp_dir[index].cp_ft[i]);    /* no phoonly attributes */
  767.     if ((temp < SPACE) || temp == 0177)    /* is it printable? */
  768.       dir_err++,
  769.       fprintf(stderr,
  770.         "\ncpmutl:illegal char 0%o in file type, slot:%d\n",temp,index);
  771.     if (temp == SPACE)
  772.       break;                /* stop on a space */
  773.     cp_name[index][j++] = temp;
  774.     }
  775.   cp_name[index][j++] = 0;
  776.  
  777.   for(j=0; cp_name[index][j] != 0; j++)    /* convert the name to lower case */
  778.     if(isupper(temp =  cp_name[index][j]))
  779.       cp_name[index][j] = tolower(temp);
  780. }
  781.  
  782. /* write the out the directory */
  783. putdir()
  784. { dirok();                /* make sure it is ok */
  785.   lwrite(CP_BASE,CP_DIRSEC,cp_dir);
  786.   dirok();                /* make sure it is ok */
  787. }
  788.  
  789. /*
  790.   read in the floppy directory
  791. */
  792. getdir()
  793. { int index,j;
  794.   lread(CP_BASE,CP_DIRSEC,cp_dir);    /* read in the directory */
  795.   for(j=0; j<2; j++)            /* allocate the directory */
  796.     set_btb(j,DIR_EXT);            /* so it isn't free */
  797.   for(index=0; index<CP_NUMDIR; index++) { /* parse each entry */
  798.     cp_name[index][0]=0;        /* initial name */
  799.     if (cp_dir[index].cp_et == DELF)    /* is this a deleted file? */
  800.       continue;                /* not interesting then */
  801.     setname(index);            /* make up cp_name */
  802.     num_ext++;                /* found one more extent in use */
  803.     if (cp_dir[index].cp_ex == 0)    /* is it the first extent? */
  804.       num_fil++;            /* yes */
  805.     for(j=0; j<DM ;j++)            /* record the disk blocks in use */
  806.       if (cp_dir[index].cp_dm[j] != 0)    /* is this entry in use? */
  807.         set_btb(cp_dir[index].cp_dm[j],index); /* yes, owned by this extent */
  808.     check_rc(index);            /* check that rc matches allocation */
  809.     }    
  810.   link_ext();                /* link extra extents to main file */
  811.   dirok();                /* make sure it is ok */
  812. }
  813.  
  814. int cur_ext;                /* current extent or -1 */
  815. int cur_pos;                /* current position in extent */
  816. char buff[CP_CLCHAR];            /* data buffer */
  817.  
  818. /* write a file */
  819. writefile(cpm_name,unix_name)
  820. char *unix_name,*cpm_name;
  821. { extern int cur_ext;
  822.   extern int cur_pos;
  823.   int i,cur_io_err;
  824.   FILE *ifile;
  825.   char *new_file_name,*scan;
  826.   char ch;
  827.   if((ifile = fopen(unix_name,"r")) == NULL) /* open the unix file */
  828.     quit(1,"\ncpmutl:open for read of unix file %s failed\n",unix_name);
  829.   new_file_name = cpm_name;        /* default to the users arg */
  830.   if(*cpm_name == '*') {        /* need to make up a name? */
  831.     new_file_name = unix_name;        /* yes, start with this one */
  832.     scan = unix_name;            /* scan for the last / */
  833.     while (*scan != 0)            /* scan the unix name */
  834.       if(*scan++ == '/')        /* is this a / ? */
  835.         new_file_name = scan;        /* yes, remember where it is */
  836.     }
  837.  
  838.   start_new_file(new_file_name);    /* prepare for some cpm_puts */
  839.   markerrs();
  840.   fprintf(stdout,"%s => [%d]%s ",unix_name,
  841.         cp_dir[cur_ext].cp_et,cp_name[cur_ext]);
  842.   fflush(stdout);
  843.   while(TRUE) {
  844.     i = fread((char *)&ch,1,1,ifile);    /* get the next input byte */
  845.     if(i == 0) break;            /* stop this at eof */
  846.     if(i != 1)
  847.       quit(1,"\ncpmutl:read error on unix file %s\n",unix_name);
  848.     if(mode == 't') {
  849.       if((ch = np(ch)) == '\n')
  850.         cpm_put('M'-64),        /* new line is cr */
  851.         cpm_put('J'-64);        /* then lf */
  852.       else
  853.         cpm_put(ch);
  854.       }
  855.     else
  856.       cpm_put(ch);
  857.     }
  858.  
  859.   if(mode == 't') cpm_put('Z'-64);    /* the finishing touch for text */
  860.   if((cur_pos % CP_CLCHAR) != 0)    /* data left in the floppy buffer */
  861.     flbuf();                /* yes, flush the buffer */
  862.   fclose(ifile);            /* close the input file */
  863.   putdir();                /* write the directory back */
  864.   reporterrs();
  865. }
  866.  
  867. /*
  868.   write routines
  869. */
  870.  
  871. cpm_put(curchar)
  872. char curchar;
  873. { extern int cur_ext;
  874.   extern int cur_pos;
  875.   extern char buff[];
  876.   int new_ext,i,buf_ptr;
  877.   if(cur_pos >= (CP_CLCHAR*DM)) {     /* this extent already full? */
  878.     new_ext = aloc_ext();        /* make the new extent */
  879.     cur_pos = 0;            /* reset position pointer */
  880.     copy_ext(&cp_dir[cur_ext],&cp_dir[new_ext]); /* copy over the name */
  881.     cp_dir[new_ext].cp_ex = cp_dir[cur_ext].cp_ex + 1;
  882.     ext_next[cur_ext] = new_ext;    /* forward link */
  883.     ext_prev[new_ext] = cur_ext;    /* back link */
  884.     cur_ext = new_ext;
  885.     setname(cur_ext);            /* set up cp_name for it */
  886.     }
  887.   if((cur_pos % SEC) == 0)        /* going into a new record ? */
  888.     cp_dir[cur_ext].cp_rc++;        /* yes */
  889.   buf_ptr = cur_pos % CP_CLCHAR;    /* where in the buffer */
  890.   if(buf_ptr == 0)            /* need a new cluster */
  891.     cp_dir[cur_ext].cp_dm[cur_pos / CP_CLCHAR] = aloc_cluster(cur_ext);
  892.   buff[buf_ptr] = curchar;        /* store the char */
  893.   if(buf_ptr == (CP_CLCHAR-1))        /* last char of the buffer? */
  894.     flbuf();                /* yes, flush the buffer */
  895.   cur_pos++;                /* advance to the next character */
  896. }
  897.  
  898. flbuf()
  899. { extern int cur_ext;
  900.   extern int cur_pos;
  901.   extern char buff[];
  902.   lwrite((cp_dir[cur_ext].cp_dm[cur_pos / CP_CLCHAR])*CP_CLUSTER+CP_BASE,
  903.       CP_CLUSTER,buff);            /* write out the buffer */
  904.   putc('.',stdout);
  905.   fflush(stdout);
  906. }
  907.  
  908. start_new_file(name)
  909. char *name;
  910. { extern int cur_ext;
  911.   extern int cur_pos;
  912.   int cur,i;
  913.   struct cp_dir_ext temp_ext;
  914.   parse_ext(&temp_ext,name);        /* parse the file name */
  915.   nuke_ext(&temp_ext);            /* nuke matching current extents */
  916.   cur_pos = 0;
  917.   cur_ext = aloc_ext();            /* make up an extent for the file */
  918.   copy_ext(&temp_ext,&cp_dir[cur_ext]);    /* give it a name */
  919.   num_fil++;                /* this extent is really a file */
  920.   setname(cur_ext);            /* set up cp_name */
  921.   dirok();                /* make sure directory is ok */
  922. }
  923.  
  924.  
  925. /* allocate a new extent */
  926. int aloc_ext()
  927. { int i,newext;
  928.   for(newext=0; newext<CP_NUMDIR; newext++) /* look through all the extents */
  929.     if(cp_dir[newext].cp_et == DELF)
  930.       break;
  931.   if(newext >= CP_NUMDIR)    /* was a free extent found? */
  932.     quit(1,"\ncpmutl:allocate extent failure, floppy directory full\n");
  933.     
  934.   num_ext++;            /* one more extent now in use */
  935.   ini_ext(&cp_dir[newext]);    /* clear it */
  936.   return(newext);        /* return the extent to the user */
  937. }
  938.  
  939. /* allocate a new cluster */
  940. int aloc_cluster(extent)
  941. int extent;
  942. { int i;
  943.   for(i=0; i<CP_ALOC; i++)
  944.     if(cp_btb[i] == -1) {
  945.     cp_btb[i] = extent;        /* record who has it */
  946.     dsk_inuse++;        /* one more cluster in use */
  947.     return i;            /* return it to the user */
  948.     }
  949.   quit(1,"\ncpmutl:allocate disk cluster failure, floppy file system full\n");
  950. }
  951.  
  952. /*
  953.   low level read/write support routines
  954. */
  955.  
  956. fl_init()
  957. { static int inited = 0;
  958.   int flmode = 0;
  959.   if(inited) return;        /* if already open leave it alone */
  960.   inited = 1;            /* yes */
  961.   if(flag('w')) flmode = 2;    /* need to write? */
  962.   if((fldes = open(flname,flmode)) < 0)
  963.     quit(1,"\ncpmutl:Open of floppy device %s failed",flname);
  964.   if(flag('i'))
  965.     fprintf(stdout," interleaving off\n"),
  966.     fflush(stdout);
  967. }
  968.  
  969. /*  logical to physical address translation */
  970. long trans(logical)
  971. register int logical;
  972. { static int xlate[] =
  973.     {1,7,13,19,25,5,11,17,23,3,9,15,21,2,8,14,20,26,6,12,18,24,4,10,16,22};
  974.   register int sector, track;
  975.  
  976.  
  977.   if ((logical < CP_BASE) ||    /* is this even in the file system? */
  978.       flag('i'))        /* is interleaving enabled? */
  979.     return(logical);        /* no, never interleaved */
  980.   sector = logical % 26;
  981.   sector = xlate[sector]-1;
  982.   track = logical / 26;
  983.  
  984.   return((track *26) + sector);
  985. }
  986.  
  987. /*
  988.   read/write block routines
  989. */
  990.  
  991. lread(startad,count,obuff)
  992. int startad, count;
  993. char *obuff;
  994. { long trans();
  995.   extern fldes;
  996.   fl_init();
  997.   while (count--) {
  998.     lseek(fldes, trans(startad)*SEC, 0);
  999.     if (read(fldes,obuff,SEC) != SEC) {
  1000.       io_err++;
  1001.       fprintf(stderr,"\ncpmutl: read sector error %d\n",startad);
  1002.       if ((startad >= CP_BASE) && /* is this in the directory? */
  1003.           (startad <  (CP_BASE + CP_DIRSEC)))
  1004.         dir_err++;        /* it counts as a directory error */
  1005.       }
  1006.     obuff += SEC;
  1007.     startad++;
  1008.     }
  1009. }
  1010.  
  1011. /* write some sectors to the floppy doing interleaving */
  1012. lwrite(startad,count,obuff)
  1013. int startad, count;
  1014. char *obuff;
  1015. { long trans();
  1016.   extern fldes;
  1017.   fl_init();
  1018.   while(count--) {
  1019.     lseek(fldes, trans(startad)*SEC, 0);
  1020.     if (write(fldes,obuff,SEC) != SEC) {
  1021.       io_err++;
  1022.       fprintf(stderr,"\ncpmutl: write sector error %d\n",startad);
  1023.       if ((startad >= CP_BASE) && /* is this in the directory? */
  1024.           (startad <  (CP_BASE + CP_DIRSEC)))
  1025.         dir_err++;        /* it counts as a directory error */
  1026.       }
  1027.     obuff += SEC;
  1028.     startad++;
  1029.     }
  1030. }
  1031.  
  1032. /* quit - a cmu local function */
  1033. quit(status,fmt,args)
  1034. int status;
  1035. char *fmt;
  1036. { _doprnt(fmt,&args,stderr);
  1037.   exit(status);
  1038. }
  1039.