#ifndef LINT char lib_replace[] = "@(#) lib/replace.c V1.0 Copyright Julian H. Stacey 89-10-04\n" ; #endif /* Symmetric implements char as signed char, hence the use of unsigned chars in some places, else a byte in afile such as 0xFF gets erroneously treated as EOF */ /* ----------------------------------------------------------------------- */ #include #include #include #ifdef ns32000 /* { */ #define BSD #endif /* } */ #ifdef BSD #include #include #else #include #endif #ifndef BSD /* { */ #include #endif /* } */ #include "ms_exe.h" /* ----------------------------------------------------------------------- */ typedef int FD ; #define FD_UNSET -1 typedef char FLAG ; #ifdef MSDOS /* { */ typedef int CKSUM ; #endif /* } */ #ifdef ns32000 /* { */ typedef short CKSUM ; #endif /* } */ /* ----------------------------------------------------------------------- */ #ifdef MSDOS #define MAXBUF 20000 /* if MAXBUF = 30000, MSC compiler generating for small model produces a binary that complains not enough room for environment */ #else #define MAXBUF 50000 #endif /* ----------------------------------------------------------------------- */ extern char **ARGV ; extern char *strcpy() ; /* ----------------------------------------------------------------------- */ /* output/tmpfile variables */ static unsigned char gbl_out_buf[MAXBUF] ; static unsigned char *gbl_out_buf_p ; /* where to write next char */ /* input file variables */ static unsigned char gbl_in_buf[MAXBUF] ; static unsigned char *gbl_in_buf_next_p ; static unsigned char *gbl_in_buf_end_p ; /* address of byte beyond buffer */ /* ----------------------------------------------------------------------- */ /* MSDOS .EXE Checksum to be written back: I dont know how msdos works out checksum, so i subtract each input word from the original checksum, and add each output word back to the original checksum */ static CKSUM gbl_orig_cksum ; /* checksum msdos file originally contains calculated by msdos linker */ static CKSUM gbl_input_data_cksum ; /*sum of words input from data segment*/ static CKSUM gbl_output_data_cksum ; /*sum of words output to data segment */ static CKSUM gbl_input_tmp_cksum ; /* byte/word buffering on input */ static CKSUM gbl_output_tmp_cksum ; /* byte/word buffering on output */ /* ----------------------------------------------------------------------- */ /* temporary file */ #ifdef MSDOS /* { */ #define TMP_NAME "/reXXXXXX.tmp" /* put temp file in ram root */ #else /* } { */ #define TMP_NAME "/tmp/replace.XXXXXX" /* unix - dont try root */ #endif /* } */ static char gbl_tmp_name[sizeof(TMP_NAME)+1] ; /* ----------------------------------------------------------------------- */ static long gbl_in_count ; static long gbl_out_count ; static FD gbl_tmp_fd ; static FD gbl_orig_fd ; static FLAG gbl_cksum_f ; static char gbl_txt_nowrite[] = "Write error on %s file.\n" ; static char gbl_txt_temp[] = "temporary" ; /* ----------------------------------------------------------------------- */ static sick(str) char *str ; { fprintf(stderr,"%s: failed %s, aborting.\n",*ARGV,str) ; perror(*ARGV) ; exit(1) ; } /* 8 & 16 bit arithmetic simulator for larger machines */ #ifdef MSDOS /* { was msdos , worked ok */ #define s_add(int_a,int_b,width) (int_a + int_b) #else /* } { */ static int s_add(int_a,int_b,bits) /* signed 8 or 16 bit addition */ int int_a, int_b ; /* must not be passed values outside the scope of signed 8/16 bit values, but does not matter if compiler implements them as longs */ int bits ; { long /* some hosts may treat ints as 8/16, & longs as 32, so stay safe with longs */ long_a, long_b, tmp, max, min ; max = (( 1L << (bits - 1)) - 1L ) ; /* ex 4 bit system 2^3-1 = 7 */ min = (-( 1L << (bits - 1)) ) ; /* ex 4 bit system -2^3 = -8 */ long_a = int_a ; long_b = int_b ; tmp = long_a + long_b ; /* to understand the next bit, draw a circle with +7 down to 0 and further round to -8 marked on it */ if ( tmp > max ) tmp -= (1L << bits) ; /* ex: 14 > 7 +14 - +16 = -2 */ else if ( tmp < min ) tmp += (1L << bits) ; /* ex: -10 < -8+16+-10=+6 */ return(tmp) ; } #endif /* } */ /* ----------------------------------------------------------------------- */ static clean_from_sig() { fprintf(stderr,"%s: Aborted on interrupt.\n",*ARGV); /* will a file be lost ? JJ */ my_unlink() ; exit(1) ; } /* ----------------------------------------------------------------------- */ static int in_c() { int amount ; amount = read( gbl_orig_fd, (char *)gbl_in_buf, (int)MAXBUF ) ; if ( amount <= 0 ) return( EOF ) ; gbl_in_buf_next_p = gbl_in_buf ; gbl_in_buf_end_p = gbl_in_buf + amount ; return( (int)*gbl_in_buf_next_p++ ) ; } #define my_getchar() (( gbl_in_buf_next_p < gbl_in_buf_end_p ) ? \ (int)*gbl_in_buf_next_p++ : in_c()) /* ----------------------------------------------------------------------- */ #define my_putchar(c) { *gbl_out_buf_p++ = c ; if (gbl_out_buf_p >= \ &gbl_out_buf[MAXBUF-1] ) my_flush() ; } static my_flush() { int amount ; /* create temporary file */ if (gbl_tmp_fd < 0 ) { /* Open temp file if necessary - if buffer is big enough, temp file creation is never done, (thats why its done here, and not earlier) */ if ( ( gbl_tmp_fd = open( gbl_tmp_name, #ifdef MSDOS /* { */ O_BINARY | #endif /* } */ O_CREAT | O_TRUNC | O_RDWR, 0200 ) ) < 0 ) { fprintf( stderr, "%s: Cannot create %s file '%s'\n", *ARGV, gbl_txt_temp, gbl_tmp_name ) ; fprintf(stderr,"Aborting.\n") ; exit(1) ; } } amount = gbl_out_buf_p - gbl_out_buf ; if ( write( gbl_tmp_fd, (char *)gbl_out_buf, amount ) != amount ) { fprintf( stderr, gbl_txt_nowrite, gbl_txt_temp ) ; my_unlink() ; fprintf(stderr,"Aborting.\n") ; exit(1) ; } gbl_out_buf_p = gbl_out_buf ; } /* ----------------------------------------------------------------------- */ static int getc_via_cksum() { int byte ; byte = my_getchar() ; if (gbl_cksum_f) { /* memorise msdos checksum in passing */ if ((gbl_in_count % 2) == 0) /* first (=high) byte of word */ gbl_input_tmp_cksum = 0xFF00 & (byte << 8 ) ; else { /* trailing low byte */ gbl_input_tmp_cksum |= 0xFF & byte ; if (gbl_in_count > MS_EXE_OVERLAY_LO) gbl_input_data_cksum = s_add(gbl_input_data_cksum, gbl_input_tmp_cksum,16) ; else if (gbl_in_count == MS_CKSUM_LO) gbl_orig_cksum = -gbl_input_tmp_cksum ; } gbl_in_count++ ; } return(byte) ; } static putchar_via_cksum(byte) char byte ; { my_putchar(byte) ; if (gbl_cksum_f) { if (gbl_out_count > MS_EXE_OVERLAY_LO) { /* add data from checksum */ if (gbl_out_count % 2) gbl_output_tmp_cksum = 0xFF00 & (byte << 8 ) ; else { gbl_output_tmp_cksum |= 0xFF & byte ; gbl_output_data_cksum = s_add(gbl_output_data_cksum, gbl_output_tmp_cksum, 16) ; } } gbl_out_count++ ; } } /* ----------------------------------------------------------------------- */ static my_unlink() { if ( gbl_tmp_fd >= 0) { (void) signal(SIGINT,SIG_IGN) ; if (close(gbl_tmp_fd)) sick("close"); if (unlink( gbl_tmp_name ) < 0 ) sick("unlink") ; gbl_tmp_fd = FD_UNSET ; (void) signal(SIGINT,SIG_DFL) ; } } static name_off_screen(name) char *name ; { int i, j ; i = strlen( name) ; for ( j = i ; j-- ; ) (void)putchar( '\b') ; for ( j = i ; j-- ; ) (void)putchar( ' ') ; for ( j = i ; j-- ; ) (void)putchar( '\b') ; } /* ----------------------------------------------------------------------- */ static buf_flush(P_buf_p, P_buf_count_u, P_remove_u, P_replace_u, P_found_u, P_occur_max_i, P_fill_c, P_binary_f, P_replace_p ) /* Flushes initial portion, then pad portion, then extension portion. Code outside this routine must copy every byte received into P_buf_p +-----------------------------------------------+ In: | REMOVE_______________________ | EXTENSION____ | +-----------------------------------------------+ Out: | REPLACE______________ | PAD__ | EXTENSION____ | +-----------------------------------------------+ */ char *P_buf_p ; unsigned P_buf_count_u ; unsigned P_remove_u ; unsigned P_replace_u; unsigned P_found_u ; int P_occur_max_i ; char P_fill_c ; FLAG P_binary_f ; char *P_replace_p ; { unsigned L_out_u ; char *L_out_p ; FLAG L_do_swap_f ; #ifdef DEBUG /* { */ printf("%s %s%d %s%d %s%d %s%d %s%d %s%c %s%c %s%s\n", "debug buf_flush: ", "|P_buf_count_u:", P_buf_count_u, "|P_remove_u:", P_remove_u, "|P_replace_u:", P_replace_u, "|P_found_u:", P_found_u, "|P_occur_max_i:", P_occur_max_i, "|P_fill_c:", P_fill_c, "|P_binary_f:", P_binary_f + '0', "|P_replace_p:", P_replace_p ) ; #endif /* } */ if (P_buf_count_u == 0) return ; /* erroneous call */ /* flush initial portion (same length as replace in diagram above) */ if (L_do_swap_f = ((P_buf_count_u >= P_remove_u) && !((P_found_u > P_occur_max_i) && (P_occur_max_i != -1)) ) ) { /* do a replacement */ L_out_p = P_replace_p ; L_out_u = P_replace_u ; } else { /* only partial match, flush input stream */ L_out_p = P_buf_p ; L_out_u = P_buf_count_u ; } while(L_out_u--) putchar_via_cksum(*L_out_p++) ; if (P_buf_count_u <= P_replace_u) return ; /* flush padding portion */ L_out_p = P_buf_p + P_replace_u ; L_out_u = (P_buf_count_u > P_remove_u) ? P_remove_u - P_replace_u : P_buf_count_u - P_replace_u ; while(L_out_u--) { if (P_binary_f) putchar_via_cksum((L_do_swap_f) ? P_fill_c : *L_out_p++); else { if (!L_do_swap_f) /* discard */ L_out_p++ ; else putchar_via_cksum(*L_out_p++) ; } } if (P_buf_count_u <= P_remove_u) return ; /* flush extension portion */ L_out_u = P_buf_count_u - P_remove_u ; while(L_out_u--) putchar_via_cksum((L_do_swap_f) ? P_fill_c : *L_out_p++); } /* ----------------------------------------------------------------------- */ /* Replace strings in a file, returns 0 if dont care how many occurences done, else returns (number requested - number replaced); exit(1) on any error */ int replace(P_name_p, P_remove_p, P_replace_p, P_extra_u, P_fill_c, P_occur_max_i, P_exact_f, P_binary_f, P_cksum_f, P_verbose_f ) char *P_name_p ; /* file name */ char *P_remove_p ; /* string to be removed */ char *P_replace_p ; /* string to be added */ unsigned P_extra_u ; /* up to so many trailing extra chars possibly occuring, to be removed or converted to fill chars */ char P_fill_c ; /* fill/pad character */ int P_occur_max_i ; /* occurences: -1 == any no, 0==none 1==1 */ FLAG P_exact_f ; /* err exit if P_occur_max_i != no of occurences detected */ FLAG P_binary_f ; /* 0==text, 1 == binary */ FLAG P_cksum_f ; /* 0==no ms_checksums, else 1 */ FLAG P_verbose_f ; /* announce file name */ { extern char *mktemp() ; extern char *malloc() ; extern syntax() ; unsigned L_remove_u, L_replace_u ; int L_length_i ; FLAG L_write_fail_f = 0 ; unsigned L_found_u = 0 ; int L_byte_i ; /* current byte from input file */ char *L_scan_p ; /* general purpose I/O pointer */ unsigned L_buf_count_u ; /* input count (increments on a character basis till a full match is detected) */ unsigned L_max_u ; CKSUM L_new_cksum ; char L_cksum_buf[2] ; char *L_in_buf_p, *L_in_buf_base_p ; /* ------------------------------------------------------------------ */ #ifdef DEBUG /* { */ printf("%s%s%s\n%s%s%s%s\n%s%u%s%c%s%d\n%s%c%s%c%s%c%s%c\n", "debug replace ", "|P_name_p:", P_name_p, "|P_remove_p:", P_remove_p, "|P_replace_p:", P_replace_p, "|P_extra_u:", P_extra_u, "|P_fill_c:", P_fill_c, "|P_occur_max_i:", P_occur_max_i, "|P_exact_f:", P_exact_f + '0', "|P_binary_f:", P_binary_f + '0', "|P_cksum_f:", P_cksum_f + '0', "|P_verbose_f:", P_verbose_f + '0') ; #endif /* } */ gbl_cksum_f = P_cksum_f ; /* export for other procedures */ /* check parameters */ if ( ( P_name_p == (char *)0 ) || ( *P_name_p == '\0' ) || ( P_remove_p == (char *)0 ) || ( *P_remove_p == '\0' ) ) { syntax() ; sick("parameters"); } if ( P_replace_p == (char *)0) P_replace_p = "\0" ; if ( P_occur_max_i < -1) sick("occurence count") ; if ((P_occur_max_i == -1) && P_exact_f) { syntax() ; exit(1) ; } L_remove_u = strlen( (char *)P_remove_p ) ; L_replace_u = strlen( (char *)P_replace_p ) ; /* check for replacement string too long */ if ( ( L_replace_u > L_remove_u ) && P_binary_f ) { *(P_replace_p + L_remove_u ) = '\0' ; fprintf( stderr, "%s: Warning: %s is binary, so replacement string truncated to %s\n", *ARGV, P_name_p, P_replace_p ) ; L_replace_u = L_remove_u ; } if (P_cksum_f && !P_binary_f) { syntax() ; exit(1) ; } /* determine number of pad characters to append after replacement string to remove tail of old string */ L_max_u = L_remove_u + P_extra_u ; /* ------------------------------------------------------------------ */ /* PREPARE TEMPORARY FILE AND POINTERS */ if ( (gbl_orig_fd = open(P_name_p, #ifdef MSDOS /* { */ O_BINARY | #endif /* } */ ( (!P_binary_f) ? O_RDONLY : O_RDWR /* fixed size so lseek will be sufficient later */ ) ) ) < 0 ) { fprintf( stderr, "%s: Cannot open %s\n", *ARGV, P_name_p); return(-10000) ; } /* copy (and change) data from P_name_p to gbl_tmp_name (or buffer) */ if (P_verbose_f) { printf( "%s", P_name_p ) ; (void)fflush( stdout) ; } /* orig_to_tmp */ gbl_tmp_fd = FD_UNSET ; /* unset, may not need to be created */ /* mktemp manual says the caller gets changed from XXXXXX, we call it once for each file, so use of strcpy protects original XXXXXX for subsequent invocations. */ (void) strcpy(gbl_tmp_name,TMP_NAME); (void) mktemp(gbl_tmp_name) ; /* initialise buffers */ gbl_in_buf_next_p = gbl_in_buf ; gbl_in_buf_end_p = gbl_in_buf ; gbl_out_buf_p = gbl_out_buf ; if ((L_in_buf_base_p = malloc(L_max_u)) == (char *)0) { fprintf(stderr,"%s: Couldnt allocate space pad memory\n", *ARGV ) ; perror(*ARGV) ; exit(1) ; } if (gbl_cksum_f) { gbl_input_data_cksum = gbl_output_data_cksum = (CKSUM)0 ; gbl_in_count = gbl_out_count = 0L ; } /* ------------------------------------------------------------------ */ /* START SCANNING INPUT - this section within these brackets can conceptually be read seperately as a smaller unit than the total procedure, in order to aid comprehension. */ { #define INIT_BUF { \ L_buf_count_u = 0 ; \ L_scan_p = P_remove_p ; \ L_in_buf_p = L_in_buf_base_p ; \ } INIT_BUF if (signal( SIGINT, clean_from_sig) < 0 ) sick("signal") ; while(1){ if ( (L_byte_i = getc_via_cksum() ) == EOF) { #define BUF_FLUSH buf_flush(L_in_buf_base_p,L_buf_count_u, L_remove_u, \ L_replace_u, L_found_u, P_occur_max_i, \ P_fill_c, P_binary_f, P_replace_p ) BUF_FLUSH ; break ; } L_byte_i &= 0xFF ; if ( gbl_cksum_f && (gbl_in_count <= (MS_EXE_OVERLAY_LO + 1))) { putchar_via_cksum((char)L_byte_i) ; continue ; } /* Cant do a a putchar if already detected enough occurences, because we want to scan input stream to detect an unexpectedly high number of input occurences */ *L_in_buf_p++ = L_byte_i ; if ((++L_buf_count_u > L_remove_u) || (*L_scan_p++ == L_byte_i)) /* access *L_scan_p after detect_f to avoid incrementing when not needed (MMU might interrupt if we go over end) */ { /* possible partial, full, or extended match, (including full or extended match not to be changed but merely noted) */ if (L_buf_count_u == L_remove_u) { /* matching string in 'P_name_p' and 'P_remove_p' */ if ((++L_found_u > P_occur_max_i) && P_exact_f) { fprintf(stderr, "%s: Error %d occurences in %s, Aborting.\n", *ARGV, L_found_u, P_name_p ) ; my_unlink() ; exit(1) ; } } if (L_buf_count_u == L_max_u) { BUF_FLUSH ; INIT_BUF } } else { /* no recognisable match, flush Note if single char in input buffer, we avoid calling flush with its time consuming 9 parameters for each byte in the file! */ if (L_buf_count_u == 1) putchar_via_cksum((char)L_byte_i) ; else BUF_FLUSH ; INIT_BUF } } (void) free(L_in_buf_base_p) ; } /* ------------------------------------------------------------------ */ /* look for odd bytes at end (not belonging to an input word) */ if (gbl_cksum_f && (--gbl_in_count > MS_EXE_OVERLAY_LO) && ((gbl_in_count % 2) == 0)) /* lone high byte in data segment */ gbl_input_data_cksum = s_add(gbl_input_data_cksum, gbl_input_tmp_cksum ,16); if ((P_occur_max_i != -1) && (P_occur_max_i != L_found_u)) fprintf(stderr, "%s: Warning: in %s, %d requested, %d detected.\n", *ARGV, P_name_p, P_occur_max_i, (int)L_found_u ) ; /* ------------------------------------------------------------------ */ /* RE OPEN ORIGINAL PRIOR TO RETURNING DATA */ if (signal( SIGINT, SIG_IGN) < 0) sick("signal") ; if ((P_binary_f) && (lseek( gbl_orig_fd, (off_t)0, 0 ) < 0) ) sick("lseek") ; else { if (close(gbl_orig_fd)) sick("close") ; /* if in text mode we may want to shorten the file, so gbl_orig_fd can not be opened just once as read+write, as read & writes wont shorten file, spare bytes will hang around unchanged at end of file */ if ((gbl_orig_fd = open( P_name_p, #ifdef MSDOS /* { */ O_BINARY | #endif /* } */ O_WRONLY | O_CREAT | O_TRUNC, 0200 ) ) < 0 ) { fprintf( stderr, "%s: Cannot recreate '%s' %s %s.\n", *ARGV, P_name_p, ",data lost (unless file was", "previously write protected), aborting.\n" ) ; my_unlink() ; exit(1) ; } } /* ------------------------------------------------------------------ */ /* WRITE DATA TO ORIGINAL FILE */ if (gbl_tmp_fd >= 0) { if (lseek( gbl_tmp_fd, (off_t)0, 0 ) < 0) sick("lseek") ; /* go back to beginning */ while ( ( L_length_i = read( gbl_tmp_fd, (char *) gbl_in_buf, (int)MAXBUF ) ) > 0 ) if ( write( gbl_orig_fd, (char *)gbl_in_buf, L_length_i ) != L_length_i ) { L_write_fail_f = 1 ; break ; } (void) close(gbl_tmp_fd) ; } /* remaining buffer */ L_length_i = gbl_out_buf_p - gbl_out_buf ; if ( L_length_i && (write( gbl_orig_fd, (char *)gbl_out_buf, L_length_i ) != L_length_i )) L_write_fail_f = 1; if ( L_write_fail_f ) { fprintf( stderr, gbl_txt_nowrite, "source" ) ; fprintf( stderr, "Aborting.\n" ) ; my_unlink() ; exit(1) ; } /* ------------------------------------------------------------------ */ /* OVERLAY NEW MSDOS .EXE CHECKSUM */ if (gbl_cksum_f) { if ((--gbl_out_count > MS_EXE_OVERLAY_LO) && ((gbl_out_count % 2) == 0)) /* lone high byte with no matching low byte */ gbl_output_data_cksum = s_add(gbl_output_data_cksum, gbl_output_tmp_cksum, 16) ; #ifdef MSDOS /* { */ #define L_SET 0 #endif /* } */ if ( (lseek(gbl_orig_fd,(off_t)MS_CKSUM_HI,L_SET) ) < 0 ) sick("lseek"); L_new_cksum = gbl_orig_cksum ; L_new_cksum = s_add(L_new_cksum, -gbl_input_data_cksum, 16); L_new_cksum = s_add(L_new_cksum, gbl_output_data_cksum, 16); L_new_cksum = -L_new_cksum ; L_cksum_buf[0] = ( L_new_cksum & 0xFF00 ) >> 8 ; L_cksum_buf[1] = L_new_cksum & 0xFF ; if (write(gbl_orig_fd,(char *)L_cksum_buf,2) != 2 ) sick("write"); } if (close(gbl_orig_fd)) sick("close") ; my_unlink() ; if (P_verbose_f) name_off_screen(P_name_p) ; return( (P_exact_f) ? (P_occur_max_i - L_found_u) : 0) ; }