/* BU.C - VERSION 2.0 --- A File Backup Utility for cp/m 80 * BDS-C version ONLY . Requires v1.50a * * Copyright Ian Ashdown * byHeart Software * 2 - 2016 West First Avenue * Vancouver, B.C. V6J 1G8 * CANADA * * Version 2.0 includes earlier modifications such as the * 'Change Disk' option, and the more recent (since v1.4) * option to simply set or reset attribute bits rather than * copy files, but is considerably re-organised with two * main purposes in mind : * * (a) to overcome the limitations for specified filename * command lines, which previously REQUIRED a user area * command -- a limitation I find simply awkward, and * (b) to reorganise the query system, so that the user may * respond to a list of filename queries and go away to leave * the program to get on with the job. I find it awkward * to have a querying system which asks if a file is needed, * goes off to do a job on it if it is, and only THEN * queries for the next. * * Ian Ashdown expressed a concern in the original Dr Dobbs' * listing about the need to search the directory for each * user code. However, with a drive selected, a '?' entry in * byte [0] of the fcb prompts both the search-file * BDOS services to return entries from EVERY user area. * I decided, then, to build a 'map' of all user areas * that are active and then, in later passes, * to zero the ex byte in the fcb as a means of collecting only * the first entry for each file name. This adds overhead * in one instance, but eliminates it in another, by skipping * the requirement to delete duplicate filerefs * * Fixed some bugs from pre 1.4 and later in hard-disk routine. * * -- Peter G. Martin May 1986 * * DEFINES, GLOBALS HISTORY AND USAGE NOTES MOVED TO BU.H */ #include /* * * * * * * * * * * MAIN BODY OF CODE * * * * * * * * * * * * * */ main(argc,argv) int argc; char *argv[]; { char in_dsk, /* drive name of input disk */ out_dsk, /* drive name of output disk */ in_drv, /* drive code of input disk */ out_drv, /* drive code of output disk */ seg_no, /* segment number for split files */ c, /* scratch variable */ *s, /* scratch string pointer */ *ptr, /* pointer to filename */ *alloc(), fcb[16], /* fcb where files sought */ in_file[15], /* fileref of current input file */ out_file[15], /* fileref of current output file */ in_fcb[33], /* fcb for input */ out_fcb[33], /* and for output */ bit_posn, /* pointer to bit to mask */ byte_posn, /* pointer to array byte to mask */ active, /* holder for current user */ mask; /* value of mask for bit */ register i,j; int files, /* Count of files */ offset, /* directory buffer index from srch */ part_written, /* flag for looping on full disk */ start_file, endv_file, first_go, x; /* for array initialisation */ /* Display program header */ printf("\nBU Version 2.0"); printf(" (May 1986 pgm) Copyright 1983, 1984"); printf(" byHeart Software\n\n"); /* Do initialisations */ /* Most flags are now treated as globals/externals (whatever) * I'm not sure if this is regarded as ok practice, but it helps * me a bit, and it seems to work all right */ all_files = FALSE; /* Copy only non-archived files */ fast_copy = FALSE; /* Copy files with verification */ hard_disk = FALSE; /* Files will not be split across backup * disks if remaining capacity of backup * disk is less than current file size */ query = FALSE; /* Backup without query */ system = FALSE; /* Assume all backups DIR for present */ user_no = ALL; /* Backup files in all user areas */ swap_flag = FALSE; att_flag = FALSE; first_go = TRUE; files = 0; next_flag=FALSE; setmem(&fcb[0],12,'?'); setmem(&fcb[12],3,NULL); setmem(&in_fcb[0],33,NULL); setmem(&out_fcb[0],33,NULL); setmem(&user_map[0],MAX_USER/8,NULL); /* Parse command line for user-selected options (if any ) */ i = 2; switch(argc) { case 1 : case 2 : error(BAD_ARGS,NULL); /* Illegal command line */ case 3 : if (*argv[i] == '-') error(BAD_ARGS,NULL); /* another one */ break; /* deal with file/drives combo no tail later */ default: ++i ; /* start with 3rd argument */ while (i < argc) { if ( *argv[i] != '-') error(BAD_OPT,argv[i]); /* Missing leading '-' */ s = argv[i]+1; while(*s) { if ( *s == 'A') /* Check all_files option */ all_files = TRUE; else if ( *s == 'F') /* fast-copy option */ fast_copy = TRUE; else if ( *s == 'H') /* hard-disk option */ hard_disk = TRUE ; else if ( *s == 'Q') /* query option */ query = TRUE ; else if ( *s == 'C') /* change disks option */ swap_flag = TRUE; else if ( *s == 'S') /* system option */ system = TRUE ; else if (isdigit( *s )) /* check for user-no. */ { user_no = *s++ - '0'; if(isdigit(*s)) user_no = user_no * 10 + *s++ - '0'; if (user_no < 0 || user_no >=MAX_USER ) error(BAD_USER,argv[i]); continue; } /* endif isdigit */ else error(BAD_OPT,argv[i]); s++; } /* enddo while *s */ i++; } /* enddo while i'P'|| *argv[2]<'A'|| *argv[2] > 'P') error(BAD_DRV,NULL); /* Illegal drive names */ if (*argv[1] == *argv[2]) { att_flag = all_files = TRUE; /* setting t's means all files */ if (hard_disk || system || fast_copy) error(SAME_DRV,NULL); /* copying options are invalid */ } /* endif argvs equ */ if(swap_flag) { printf("\nInsert disk(s) into relevant drive(s)."); puts ("\nWhen ready, hit 'C' to continue : "); do { c = toupper(getchar()); printf("\b%c",c); } /* enduntil not 'C' */ while(c != 'C'); printf("\bContinuing...\n"); } /*endif swap_flag */ /* Save entry drive and user codes */ bdos(RESET_DRV,NULL); /* playing safe */ ent_drv = bdos(GET_DRV); ent_user = bdos(USER_CODE,0xff); /* Give usage for attribute setting */ if (att_flag) { /* Display instructions on t' bit setting * and get generic pattern (if it applies) * .... aborting if it asks for no changes */ usage_att(query,in_fcb,first_go); /* <= asks for att pat if not -Q */ } /* Calculate input and output drive codes */ in_drv = (in_dsk = *argv[1] ) - 'A' + 1; out_drv = (out_dsk = *argv[2]) - 'A' + 1; /* Set default drive to input drive */ bdos(SEL_DRV,in_drv - 1); /* Set up bit map for users active -- to 'user_no' if -n specified */ if (user_no != ALL) { if(user_no == 0) user_map[0] = 1; else user_map[user_no/8] |= ( 1 << (user_no % 8) ); } else { fcb[0] = '?'; /* check ALL user areas */ /* Where no user area has been nominate, set up a map * of all active users, ignoring those * for erased files, or for files with archive bit set * if we are doing backup and the -A flag is not set * Unfortunately, this doesn't seem to work for any * specified fileref -- at least on my system * So we don't collect filenames here -- just users */ while( (offset = srch_file(fcb,next_flag)) != ERROR) { next_flag = TRUE; fcb[0] = '?'; /* srch_next seems to change this */ ptr = 0x80 + (offset * 32); if( (ptr[0] != 0xe5) && (!(ptr[11] & 0x80) || all_files) ) { active = ptr[0]; bit_posn = (active % 8); byte_posn = (active/8 ); if(bit_posn != 0) user_map[byte_posn] |= ( 1 << bit_posn); else user_map[byte_posn] |= 1; } } /* endwhile */ } /* end userall else */ /* Check thru our map for files and collect names * This time, however, we use a specified 'dr' (and fileref * if it is supplied) and by zeroing the 'ex' byte * we should get ONLY the first extent of each file * returned by srch_file */ if(query) { puts("Checking files required ....\n\n"); puts("User : File (Specify which to deal with) \n\n"); } else printf("\nACTIVE USER AREAS FOR FILES :\n"); root.next = NULL; fref_1 = &root; for (cur_user = 0 ; cur_user < MAX_USER; cur_user++) { bit_posn = (cur_user % 8); byte_posn = (cur_user/8); if(bit_posn == 0) mask = 1; else mask = ( 1 << bit_posn); if( (user_map[byte_posn] & mask) == 0) continue; else if(!query) printf(" %2d",cur_user); bdos(USER_CODE,cur_user); next_flag = FALSE; /* search first indicator */ fcb[0] = NULL; fcb[12] = NULL; /* zeroing the 'ex' byte in fcb */ while ( (offset = srch_file(fcb,next_flag)) != ERROR) { next_flag = TRUE; ptr = 0x80 + (offset * 32); movmem(ptr,sf_cur,12); if(sf_cur[0] != 0xe5 && (!(sf_cur[11] & 0x80) || all_files) ) { if(query) { if(!(att_flag)) { printf(" %2d ",cur_user); printf(" %s O.K. to backup ? [Y/N] : ", sf_cur+1 ); c = toupper(getchar()); printf("\b%c",c); if(c == 'Y') puts("es\n"); else { puts("= No\n"); continue; /* loop for not wanted */ } /* endif/else c==Y */ } /*endif !att */ if (att_flag && (get_atts(sf_cur,first_go) == 0) ) continue; /* loop for no change */ } /* endif query */ fref_1->next = alloc(sizeof(root)); fref_1 = fref_1->next; movmem(sf_cur,fref_1->name,12); /* add file to list */ fref_1->next = NULL; /* mark end of linked list */ ++files; /* update the count */ } /* endif !=0xe5 etc */ } /*end while */ } /*end for */ first_go = FALSE; /* make sure of this for get_atts() */ puts("\n\n *****************************\n\n"); if(files == 0 ) { printf("No files on main drive are affected."); if(user_no != ALL) printf("\b in user area %d.\n",user_no); else putchar('\n'); reset(); /* reset user and drive codes to entry values */ exit(0); } /* Display file copy header */ printf("Number of files : %d\n\n",files); printf("User:"); if(att_flag) printf(" File(s) for attribute setting "); else { printf(" Files being copied to Drive %c:",out_dsk); /* Initialize current input and output fileref templates */ in_file[0] = in_dsk; out_file[0] = out_dsk; in_file[1] = out_file[1] = ':'; in_file[10] = out_file[10] = '.'; in_file[14] = out_file[14] = '\0'; /* Initialize current input and outpub FCB templates */ out_fcb[0] = out_drv; } /* endif attflag */ printf("\n\n"); in_fcb[0] = in_drv; /* For all validated filerefs do.... */ for (cur_user = 0 ; cur_user < MAX_USER; cur_user++) { bit_posn = (cur_user % 8 ); byte_posn = (cur_user/8 ); if(bit_posn == 0) mask = 1; else mask = ( 1 << bit_posn); if(user_map[byte_posn] & mask == 0 ) /* is user in our map ?*/ continue; /* LOOP if not */ bdos(USER_CODE,cur_user); /* ELSE Set user code to 'cur_user' */ fref_1 = &root; /* start on list again */ part_written = TRUE; while(fref_1->next) { fref_1 = fref_1->next; /* Move on to next file name */ if(fref_1->name[0] == cur_user) { /* Update the current input FCB */ movmem(fref_1->name+1,in_fcb+1,11); in_fcb[12] = NULL; if(att_flag) { printf(" %2d %s ",cur_user,in_fcb+1); if(query) { puts("being set to "); showatts(in_fcb); putchar('\n'); } else { get_atts(in_fcb,first_go); } bdos(SET_ATT,in_fcb); } else /* routine to fix attributes so files can be opened */ { movmem(fref_1->name+1,out_fcb+1,11); out_fcb[9] &= 0x7f; /* r/o attrib */ if(!(system)) out_fcb[10] &= 0x7f; /* sys attrib */ /* Set the Archive attribute bit of the FCBs to indicate * that the file has been backed up. Only effective when * bdos has FIXED it.... */ in_fcb[11] |= 0x80 ; out_fcb[11] |= 0x80 ; hacking(in_file+2,in_fcb+1); hacking(out_file+2,out_fcb+1); /* Strip high order bits off filerefs to form ASCII chars */ for(j = 2; j<= 13; j++) { in_file[j] &= 0x7f; out_file[j] &= 0x7f; } /* Display the filerefs */ printf(" %2d %s --> %s\n", cur_user,in_file,out_file); /* Copy file from the input disk to the output disk */ if(hard_disk) /* split file over backup disks if necessary */ split_copy(in_fcb,out_fcb,in_file,out_file); else one_copy(in_fcb,out_fcb,in_file,out_file); } /* endif att_flag/else */ } /* endif cur_user */ } /* enddo while fref_1 */ } /* end FOR users etc */ if(!(att_flag)) { printf("\nCopying complete...\n"); printf("BACKUP files are set R/O and ARC(hived)\n"); } else printf("\n Attribute setting DONE.\n"); if(swap_flag) { printf("Ensure system disk in original drive and hit a key..."); while (!(c=getchar())) ; } reset(); /* Reset user and drive codes to entry values */ } /* end of main function 7 May 1986 P.G.M. */