/*------------------------------------------------------------------ | SR | | 20 Feb 85 rhw Fix the bug where -verbose 1st char of line | displayed is missing. Change \n character | substitution in clean_s() to a simple LF | rather than CRLF. Also remove the -b param | since \s is available. | 19 Feb 85 rhw Modify to search and replace version. I/O | changed to stream from unbuffered. | 13 Feb 85 rhw edit_names() should mask out the chars in | the file name with 0x7F since there may be | bits set in the high order bytes of the name. | 26 Jan 85 rhw Turbodos 1.4 fixup. Re-interpretation of | the fcb field ex, extent, and the actual | way that search for next works, caused me | to remove the test for extent 00 as criteria | for next file. Search for next actually | seems to look for the first extent of a | file, no matter what the ex field contains. | 26 Jan 85 rhw 1.06 conversion. Add fcntl.h, io.h, which | fixes up the definition of a struct fcb. | 01 Aug 84 Add msg in verbose mode to let user know | we're searching the file, each file, in | order. | 26 Jul 84 Sort the file list into alphabetic order. | 20 Jul 84 Add -verbose param. | _________ | | SF is a string search and replace utility which allows | wildcard file specification to identify files on the | disk to search. | | Normally the program outputs only the list of files | it finds a match in, and no other stuff so that | io re-direction can make a file list to a file for | editing. | | -f | -s | -r | -b | -v verbose list of search finds. ------------------------------------------------------------------*/ #include "stdio.h" #define CTRLC 0x03 /* ^C is a 3 */ #define N_FILES 128 /* max number of files to find */ #define DMASIZE 128 /* buffer size for dir dma */ #define TOPRINT 79 /* Max bytes to print for each find if -v list is on */ #define LIST_ONLY 0 /* Just list the file names */ #define SUB_LIST 1 /* Substitute into a mask the file name */ #define SEARCH 2 /* Search for a string */ #define SANDR 3 /* Search for string and replace */ /*------------------------------------------------------------------ | GLOBALS ------------------------------------------------------------------*/ int fcbinit(); /* FCB constructor function */ struct fcb *searchfcb; /* FCB space for the file spec. input. */ char *fnames[N_FILES]; /* Pointers to the file names found to match the wildcard specification */ char *dma; /* DMA buffer for directory sector transfer */ char wildname[15]; /* The converted input file name for the param -f */ char *tempname = "$DELETE$.$$$"; int options=LIST_ONLY; /* number figured from input params to be option selection. See defines for legit values */ char *f; /* Points to the -f argument */ char *r; /* Points to the -r argument */ char *s; /* Points to the -s argument */ int verbose=0; /* True if user wants to display the context of finds. -v param. */ int cvtupper=1; /* True if there is no \u or \l to force conversion of the search and replace strings. I.e., we assume that conversion to upper case for string comparison is wanted, but this is set false if either the -s or -r params contain \u or \l. */ /*------------------------------------------------------------------ | MAIN ------------------------------------------------------------------*/ main(argc, argv) int argc; char *argv[]; { char *is_f(), *is_s(), *is_r(); /* Reserve space for the directory buffer */ dma = malloc(DMASIZE); /* Reserve space for a CP/M fcb */ searchfcb = malloc(36); if ( argc == 1 ) { /* Just print instructions and exit */ explain(); exit(0); } if ( ! (f = is_f(argc,argv)) ) { printf("\nMissing -f argument."); exit(-1); } if ( s = is_s(argc,argv) ) options = SEARCH; if ( r = is_r(argc,argv) ) options = SANDR; verbose = is_v(argc,argv); /* Set the verbose argument. */ fcbinit(f,searchfcb); /* Sets up the fcb argument for search */ file_list(); /* Generates a list of file names in the array fnames[] */ listem(); /* Output the list or whatever option output is selected */ } /*------------------------------------------------------------------ | is_f(argc,argv) | | Return 0 if -f is not found, a pointer to the file name | if it is found. ------------------------------------------------------------------*/ char *is_f(argc,argv) int argc; char *argv[]; { int i,j; char *coloninc; /* An increment to use in the test for filling in ?'s for *'s */ char *cp, *k, *dot; /* find the -f argument */ for ( i=1; i0 && j<14)); k++) { switch ( *k ) { case '*': if ( dot = index(wildname,'.') ) { while ( cp-dot < 4 ) { *(cp++) = '?'; j++; } } else { /* is still first f1...f8 */ while ( j < 8 + coloninc ) { *(cp++) = '?'; j++; } } break; default: *(cp++) = *k; j++; break; } } return wildname; } else return 0; /* Argument not found */ } /*------------------------------------------------------------------ | is_s(argc,argv) | | Return 0 if -s is not found, a pointer to the search string | if it is found. ------------------------------------------------------------------*/ char *is_s(argc,argv) int argc; char *argv[]; { int i,j; /* find the -s argument */ for ( i=1; i= argc-1 ) { /* no -s */ printf("\n-Replace requested without -s parameter."); exit(-1); } return argv[i+1]; /* ...as the -r string pointer */ } else /* No replace string */ return 0; } /*------------------------------------------------------------------ | is_v(argc,argv) | | Return 0 if -v is not found, 1 if is found. ------------------------------------------------------------------*/ int is_v(argc,argv) int argc; char *argv[]; { int i,j; /* find the -v argument */ for ( i=1; ij. -------------------------------------------------------------------*/ f_comp(i,j) unsigned i,j; { return strcmp(fnames[i],fnames[j]); } /*------------------------------------------------------------------ | sch_first() | | Search for first file to match input spec. Returns 0 if | not found, ptr to dir entry otherwise. ------------------------------------------------------------------*/ char *sch_first() { int a; /* Returned parameter from bdos calls */ /* Set dma to our buffer */ bdos(26, dma); /* Search for first */ a = bdos(17, searchfcb) & 0xFF; if ( a == 0xFF ) { return 0; } else { return (a << 5) + dma; } } /*------------------------------------------------------------------ | sch_next() | | Search for next file to match input spec. Returns 0 if | not found, ptr to dir entry otherwise. ------------------------------------------------------------------*/ char *sch_next() { int a; /* Returned parameter from bdos calls */ /* Search for next */ a = bdos(18, 0) & 0xFF; if ( a == 0xFF ) { return 0; } else { return (a << 5) + dma; } } /*------------------------------------------------------------------ | edit_names() | | Edit the file names in fnames[] so that the blanks are | compressed out, a period is inserted, and the name is | delimited with a zero. ------------------------------------------------------------------*/ int edit_names() { int i; char *cp, *k; for ( i=0; fnames[i]; i++ ) { *(fnames[i]+12) = 0; /* delimit the name */ /* Put in the period */ *(fnames[i]+11) = *(fnames[i]+10); *(fnames[i]+10) = *(fnames[i]+9); *(fnames[i]+9) = *(fnames[i]+8); *(fnames[i]+8) = '.'; /* Go through the name char by char. cp is always <= k, never larger. */ for ( k=cp=fnames[i]; *k; k++ ) { switch ( *k & 0x7F ) { case ' ': break; default: *cp = *k & 0x7F; cp++; break; } } *cp = 0; /* delimit the possibly shorter name */ /* Remove period if there is no extent on name */ if ( cp = rindex(fnames[i], '.') ) if ( ! *(cp+1) ) *cp = 0; } } /*------------------------------------------------------------------ | listem() | | List the files in the fnames[] list. ------------------------------------------------------------------*/ int listem() { int i; switch ( options ) { case LIST_ONLY: for ( i=0; fnames[i]; i++ ) printf("%s\n", fnames[i]); break; case SEARCH: case SANDR: search(); break; } /* ......end switch on options */ } /*------------------------------------------------------------------ | search() | | Search the list of files for the string specified in | the global variable s. ------------------------------------------------------------------*/ int search() { extern char *clean_s(); static int hit; /* True if search makes a match */ static int i,c,k; static int newlines; /* counted number of newlines */ static long lastnewline; /* File position of the last CR or LF, or begin of file. */ static long onnomatch; /* File position to restart search on unsuccessful compare */ static long pickup; /* File position */ static char *ss, *rs, *ssp; static char filename[20]; /* filename big enough for the drive spec. */ static char tempbuf[20]; /* filename big enough for the tempname drive spec. */ FILE *fd, *fr, *fopen(); long ftell(); /*....................NOTE................. Manx does not always recognize EOF when the file is terminated with a single 1A and the buffer is not padded with 1A's. Therefore, this program checks for 1A as well as EOF. .........................................*/ /* Clean up search string */ ss = clean_s(s); /* Clean up replace string */ rs = clean_s(r); /* While there is a filename in the list do the following ....... */ for ( i=0; fnames[i]; i++ ) { userabort(); /* Give user a chance to abort. */ /* If there was a drive specified in the input file spec then add that to the file name. */ if ( index(f,':') ) { /* Copy in the whole of the file spec input by user, then copy the resultant file name in just behind the drive spec. Do it for both the original and copy file, even though there may be no copying. */ strcpy(filename,f); strcpy(tempbuf,f); strcpy(index(filename,':')+1, fnames[i]); strcpy(index(tempbuf,':')+1, tempname); } else { strcpy(filename,fnames[i]); strcpy(tempbuf,tempname); } if ( (fd = fopen(filename,"r")) == NULL ) { printf("\nError opening file %s\n", fnames[i]); exit(-1); } /* If doing a replace then open a dummy file. */ if ( options == SANDR ) { if ( (fr = fopen(tempbuf,"w")) == NULL ) { printf("\nError opening file %s\n", tempbuf); exit(-1); } } /* Let user know we're working if verbose is on. */ if ( verbose ) printf("\n\nSearching %s", filename); /* Init the position of last new line. */ lastnewline = 0L; newlines = 1; hit = 0; /* Assume no match */ /* SEARCH FILE AND REPORT MATCHES */ while ( (k=c=getc(fd)) != EOF ) { if ( c == 0x1A ) break; if ( cvtupper ) c = toupper(c); /* Uppercase */ if ( c == '\n' ) { newlines++; lastnewline = ftell(fd)-1L; } if ( c == *ss ) { /* Remember where to get back to */ onnomatch = ftell(fd); /* identify possible match */ ssp = ss; while ( (c=getc(fd)) != EOF ) { if ( cvtupper ) c = toupper(c); /* Uppercase */ if ( c != *(++ssp) ) break; if ( c == 0x1A ) break; } if ( *ssp == 0 ) { hit++; if ( c != EOF && c != 0x1A ) ungetc(c,fd); if ( verbose ) { pickup = ftell(fd); /* pick up here after showing line */ show_hit(filename,fd,lastnewline,hit,newlines); fseek(fd,pickup,0); } if ( options == SANDR ) { /* write replace string */ for (ssp=rs; *ssp; ssp++ ) putc(*ssp,fr); } } else { /* Hey! it wasn't a match after all */ if ( options == SANDR ) putc(k,fr); fseek(fd,onnomatch,0); } } else { /* THIS CHAR DOESN'T MATCH ss */ if ( options == SANDR ) putc(k,fr); /* write to copy */ } } /* No need to print file name if verbose is on. */ if ( hit && ! verbose ) printf("%s\n",fnames[i]); fclose(fd); if ( options == SANDR ) { fclose(fr); if ( unlink(filename) != 0 ) { printf("\nCan't delete %s, FATAL ERR", filename); exit(-1); } if ( rename(tempbuf, filename) == -1 ) { printf("\nCan't rename %s, FATAL ERR", tempbuf); exit(-1); } } } printf("\n"); /* Last line should have a CRLF */ } /*------------------------------------------------------------------ | show_hit(file_name, fd, last new line pos, where hit is, | number_of_hits, number_of_newlines) | | Show the context line of the match. Back up to the | last newline, and print to next newline. ------------------------------------------------------------------*/ int show_hit(fname,fd,lastnewline,hit,newlines) char *fname; FILE *fd; long lastnewline; /* file position of the last newline */ int hit,newlines; /* numbers of these things */ { long bytepos; /* where we are in the file */ extern int userabort(); extern long ftell(); int c; userabort(); /* Give user chance to abort. */ bytepos = ftell(fd); printf("\n\n%s #%-4d @ byte%7ld (%06lx) line%5d", fname, hit, bytepos, bytepos, newlines); fseek(fd,lastnewline,0); /* position to print line */ printf("\n"); /* Flush any starting newlines now */ if ( (c=getc(fd)) != '\n' ) ungetc(c,fd); /* ok, put it back */ while ( (c=getc(fd)) != EOF ) { /* quit when find eof, newline, or CR */ if ( c == 0x1A || c == '\n' || c == '\r' ) break; /* print ascii chars and tabs only */ printf("%c", (((c>31 && c<128) || c=='\t') ? c : '.') ); } } /*------------------------------------------------------------------ | clean_s(s) | | Clean up the search or replace string input and return | pointer to the | actual string to use in the search. A number of | substitutions are performed. ------------------------------------------------------------------*/ char *clean_s(s) char *s; { char *cp,*searchbuf; int upper=0,lower=0; /* case'ing flags */ /* searchbuf size is the most that can be typed into a command line so it is plenty big for the resultant search or replace string */ searchbuf = calloc(1,128); /* clears buffer to 0 */ for ( cp=searchbuf; *s; s++ ) { switch ( *s ) { case '\\': /* if \ is last char then throw away */ if ( ! *(++s) ) s--; else { /* substitute */ switch ( *s ) { /* 2nd letter */ case 'N': *(cp++) = '\n'; break; case 'R': *(cp++) = '\r'; break; case 'T': *(cp++) = '\t'; break; case 'B': *(cp++) = '\b'; break; case 'S': *(cp++) = ' '; break; case '\\': *(cp++) = '\\'; break; case 'F': *(cp++) = '\f'; break; case 'U': upper = 1; lower = 0; cvtupper = 0; break; case 'L': upper = 0; lower = 1; cvtupper = 0; break; } } break; default: /* Copy the character */ *cp = *s; if ( upper ) *cp = toupper(*cp); if ( lower ) *cp = tolower(*cp); cp++; break; } } *cp = 0; /* delimit search string */ return searchbuf; } /*------------------------------------------------------------------ | userabort() | | Determine if user has typed ^C and abort if so. ------------------------------------------------------------------*/ int userabort() { if ( bdos(6,0xFF) == CTRLC ) { /* ^C Char has been typed */ printf("\n.....user aborted.\n"); exit(0); } return; /* Continue on our way. */ }