home *** CD-ROM | disk | FTP | other *** search
- #include <conio.h>
- #include <dos.h>
- #include <ctype.h>
- #undef tolower /* use function version instead of macro version */
- #undef toupper
- #include <stdarg.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- /*------------------------- PATCH22 ---------------------------------------
- * just compile as: cl patch22.c
- *
- * exit value (byte):
- * -5: error in patch command string
- * -4: file with patch commands not found
- * -3: file to be patched not found
- * -2: error in patch commandline (if not silent help is displayed)
- * -1: not used (reserved for calling through spawn functions)
- * >= 0: total no of patches made
- *-------------------------------------------------------------------------*/
-
- /*----------------------------- Revision history ---------------------------
- * 8-08-91: Released version 1.2
- * 22-07-91: - The date/time stamp of a file is reset if any patches are made.
- * - Added (simple) password protection option.
- * 7-07-91: - If user chooces Quit, now breaks out of while loop, instead
- * of spooling to end of file.
- * - Corrected bug pointed out by Ernst Raedecker of Amsterdam:
- * find function did not allow for search string matching within
- * itself. (See patch22.doc)
- * - Now the next search continues one byte after the start of the
- * previous match.
- * 12-05-91: cctos() converts character following \ to lowercase, changed
- * ffindi() slightly.
- * 11-05-91: Now checks for progress in conditions field (+A000 led to
- * infinite loop). Added case insensitive option.
- * 11-04-91: Added <Q>uit option to verbose patching.
- * 5-04-91: Replaced function Abort by message (using stdarg).
- * Now continues with next patch if taking patches from file and
- * file to be patched not found.
- * 28-02-91: Fixed bug in ffindf function (the first non-matching char after
- * a match was not used as the starting char in the next search).
- * 22-02-91: Added error message if 0 patches requested.
- * 18-02-91: Command line patch didn't work, bug fixed.
- * 03-02-91: Added \w char escape codes, replaced mallocs by static arrays,
- * using scanf to read \x##, \w####, \### (see NB.2 in cctos).
- * 02-02-91: Less silent; removed error in scanning upto ?; added missing
- * breaks in switching for \ char escape codes.
- *--------------------------------------------------------------------------*/
-
-
- /*=========================== global help ==================================*/
- #define ERR_EXIT { fprintf(stderr, sUsage, sProgram, sVersion, sAuthor, \
- sProgram, sProgram, sProgram, sProgram); exit(0); }
-
- char *sProgram = "PATCH22";
- char *sVersion = "version 1.2";
- char *sAuthor = "(C) J.A. Doornik, 8 August 1991.";
-
- char sP22[] = { '«','p','2','2','_','»' }; /* signature */
- char sPassword[] = "\0 gf ggv aewf bvsbago"; /* password: 21 chars+\0 */
- /* leading \0 indicates no password; avoid .exe compression */
-
- char *sUsage =
- "\n%s %s: patches any file %s\n"
- "\nUsage:\t%s fname +##*##si\?search_text\?new_text\?"
- "\n or:\t%s @fname\n"
- "\nfname\tfile to be patched"
- "\n+##\t+ve or -ve offset from the start of the search text"
- "\n*##\tmaximum number of patches"
- "\ns\tsilent patch, default is confirm each patch"
- "\ni\tcase insensitive patch, default is case sensitive"
- "\n@fname\tfile with (multiple) %s commands, with format:"
- "\n\tfname +##*##s\?search_text\?new_text\?"
- "\ntext\tC-type character constant, possibly including:"
- "\n\t\\a (07h), \\b (08h), \\f (0Ch), \\n (0Ah), \\r (0Dh), \\t (09h), \\v (0Bh);"
- "\n\t\\### (octal byte, not more than 3 digits);"
- "\n\t\\w#### (hexadecimal word, not more than 4 digits);"
- "\n\t\\x## (hexadecimal byte, not more than 2 digits);"
- "\n\t\\h (the rest of the text contains two-digit hexadecimal bytes)."
- "\n\tThe characters \\ \? \' \" must be written as: \\\" \\\\ \\\? \\\' \\\"."
- "\n\nEg.\t%s test *1\?\h0d0a00\?\\r\\n\\0\?"
- "\nwhich makes one change, where the replace text equals the search text.\n";
-
-
- /*=========================== global types =================================*/
- typedef unsigned char byte; /* b */
- typedef unsigned short bool; /* f */
- typedef unsigned short word; /* w */
-
- #define FALSE 0
- #define TRUE !FALSE
-
- struct Patch22 /* structure to store all patch info */
- {
- byte *sSearch, *sNew; /* search text and new text */
- int cNew, cSearch; /* # of bytes in search text and new text */
- int iMaxpatch, cPatch; /* maximum and actual number of patches */
- int iMaxfind; /* maximum number of finds */
- long lOffset; /* offset from start of search string */
- };
-
- /*=========================== global data ==================================*/
- #define MAXPATLEN 1024 /* maximum length of a patch text */
- static bool fVerbose_ = TRUE;
- static int iColor_hi = 0x001f, iColor_lo = 0x0017;
- static char *s16dots = "∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙";
- static char *s16dots3 = "∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ";
-
- int ffindfn(FILE *f, byte *s, int slen); /* prototypes */
- int ffindfi(FILE *f, byte *s, int slen);
- int (* ffindf)(FILE *, byte *, int) = ffindfn; /* case sensitive find */
-
- /* --------------- files and internal buffer for in/output --------------- */
- #define BUFLEN 20480 /* 20K buffer */
- static byte inbuf_[BUFLEN];
- static FILE *infile_, *comfile_; /* files are global variables */
- unsigned uFdate, uFtime; /* date and time of file to be patched */
-
- /*--------------------------- procedure message ----------------------------*/
- void message(int exitcode, char *msg, ...)
- {
- va_list args;
-
- fprintf(stderr, "%s error: ", sProgram);
- va_start(args, msg); vfprintf(stderr, msg, args); va_end(args);
- fprintf(stderr, ".\n");
-
- if (exitcode != 0) exit(exitcode);
- }
- /*--------------------------- end procedure message ------------------------*/
-
-
- /*------------------ open_infile/close_infile/open_comfile -----------------*/
- int open_infile(char *infname) /* get file to be patched */
- {
- if ( (infile_ = fopen(infname, "r+b")) == NULL) /* read & write, binary */
- { message(0, "cannot open file %s", infname);
- return(FALSE);
- }
- _dos_getftime(fileno(infile_), &uFdate, &uFtime); /* get file date/time */
- setvbuf(infile_, inbuf_, _IOFBF, BUFLEN);
- return(TRUE);
- }
-
- void close_infile(int npatches) /* close patched file */
- {
- if (npatches > 0)
- _dos_setftime(fileno(infile_), uFdate, uFtime); /* reset file date */
- fclose(infile_);
- }
-
- int open_comfile(char *comfname) /* get file with patch commands */
- {
- if ( (comfile_ = fopen(comfname, "r")) == NULL) /* read */
- { message(0, "cannot open file %s", comfname);
- return(FALSE);
- }
- return(TRUE);
- }
- /*------------------ END open_infile/close_infile/open_comfile -------------*/
-
-
- /*------------------------------- getpatch ---------------------------------*/
- int getpatch(FILE *f, char *infname, byte *s)
- { /* gets fname +##*##s?search_text?new_text? from a file */
- int ch, len;
-
- do
- { fscanf(f, " "); /* skip white space */
- ch = 0; fscanf(f, ";%n", &ch); /* check if there is a ; */
- if (ch > 0) fscanf(f, "%*[^\n]"); /* skip comment lines upto \n */
- } while (!feof(f) && ch > 0);
- if (fscanf(f, " %s ", infname) != 1) return(EOF);
-
- len = MAXPATLEN - 1; /* read upto first question mark */
- while (--len > 0 && (ch = fgetc(f)) != EOF && ch != '\?')
- if (ch != '\n') *s++ = ch;
- *s++ = '\?';
- len = MAXPATLEN - 1; /* read upto second question mark */
- while (--len > 0 && (ch = fgetc(f)) != EOF && ch != '\?')
- { if (ch == '\\')
- { *s++ = ch; *s++ = fgetc(f); --len; /* read char. escape code */
- }
- else if (ch != '\n') *s++ = ch;
- }
- *s++ = '\?';
- len = MAXPATLEN - 1; /* read upto third question mark */
- while (--len > 0 && (ch = fgetc(f)) != EOF && ch != '\?')
- { if (ch == '\\')
- { *s++ = ch; *s++ = fgetc(f); --len; /* read char. escape code */
- }
- else if (ch != '\n') *s++ = ch;
- }
- *s++ = '\?';
- *s = '\0';
-
- if (ch == EOF) message(-5, "unexpected end of command file");
- return(0);
- }
- /*------------------------------- END getpatch -----------------------------*/
-
-
- /*---------------------- cctos/parse_patch ---------------------------------
- * NB.1 for octal and hexadecimal escape sequences:
- * - octal can not be more than 3 digits;
- * - hexadecimal can not be more than 2 digits;
- * (C 6.0: hexadecimal escape sequences use every potential hexadecimal
- * digit following the \x, so "\x005float" is "_loat".)
- * NB.2 Usage of %n format in scanf:
- * Consider reading: num = 0; sscanf(cc, "%3o%n", &ch, &num);
- * where cc = "01"
- * Then scanning stops at the '\0', before the %n is reached, and the
- * num argument is NOT set. This seems a compiler bug.
- * If no characters are read, num is not set to zero.
- *--------------------------------------------------------------------------*/
- static int cctos(byte *cc) /* convert character constant to string */
- {
- static byte src[MAXPATLEN + 5];
- byte *s; int len, num; unsigned int ch;
-
- strcpy(src, cc);
- s = cc; cc = src; /* input from static src, output overwrites argument */
- strcat(cc, "\a\a\a\a"); /* to avoid problems as pointed out in NB.2 */
- /* the entry string is a character string which does not contain */
- /* chars < 32 (except '\0'), use '\a' to indicate end */
-
- for (len = 0; *cc != '\a'; cc++, s++, len++) /* scan upto \a */
- { if(*cc != '\\')
- *s = *cc;
- else
- { switch (tolower(*++cc))
- { case 'a': *s = '\a'; break;
- case 'b': *s = '\b'; break;
- case 'f': *s = '\f'; break;
- case 'n': *s = '\n'; break;
- case 'r': *s = '\r'; break;
- case 't': *s = '\t'; break;
- case 'v': *s = '\v'; break;
- case '\\': *s = '\\'; break;
- case '\?': *s = '\?'; break;
- case '\'': *s = '\''; break;
- case '\"': *s = '\"'; break;
- case 'h':
- for (++cc; *cc != '\a'; cc += 2, s++, len++)
- { num = 0; sscanf(cc, "%2x%n", &ch, &num);
- *s = (byte) ch;
- if (num != 2)
- message(-5, "Error in \\h escape sequence: %s", cc);
- }
- s--; len--;
- break;
- case 'w': /* hexadecimal word */
- num = 0; sscanf(++cc, "%4x%n", (unsigned int *)s, &num);
- s++; len++; /* read word, not byte */
- if (num == 0)
- message(-5, "Error in \\w escape sequence: %s", cc - 2);
- cc += num - 1;
- break;
- case 'x': /* hexadecimal byte */
- num = 0; sscanf(++cc, "%2x%n", &ch, &num);
- *s = (byte) ch;
- if (num == 0)
- message(-5, "Error in \\x escape sequence: %s", cc - 2);
- cc += num - 1;
- break;
- default: /* read octal byte, else error in string */
- num = 0; sscanf(cc, "%3o%n", &ch, &num);
- *s = (byte) ch;
- if (num == 0)
- message(-5, "Unknown escape sequence: %s", cc - 1);
- cc += num - 1;
- }
- }
- }
- return(len);
- }
- void parse_patch(byte *s, struct Patch22 *p22)
- { /* parse +##*##s?search_text?new_text? */
- byte *s1; bool fignorecase = FALSE;
-
- fVerbose_ = TRUE;
- p22->cNew = p22->cSearch = 0;
- p22->iMaxpatch = p22->iMaxfind = -1; p22->lOffset = 0L;
-
- while (*s != '\0' && *s != '\?') /* read upto first ? */
- { if (*s == '+' || *s == '-' || isdigit(*s))
- p22->lOffset = strtol(s, &s1, 0);
- else if (*s == '*')
- { if ( (p22->iMaxpatch = (int) strtol(s + 1, &s1, 0)) == 0)
- message(-5, "0 patches requested");
- }
- else if (*s == 's' || *s == 'S')
- { fVerbose_ = FALSE;
- for (s1 = s + 1; isspace(*s1); s1++) ;
- }
- else if (*s == 'i' || *s == 'I')
- { ffindf = ffindfi; /* case insensitive search */
- fignorecase = TRUE;
- for (s1 = s + 1; isspace(*s1); s1++) ;
- }
- else if (*s != '\?')
- message(-5, "\? expected");
-
- if (s1 == s) /* now s1 points to next pos, check if not same as old */
- message(-5, "unable to scan conditions field");
- s = s1;
- }
-
- p22->sSearch = ++s; /* skip first ? */
- while (*s != '\0' && *s != '\?')
- { if (*s == '\\') s++;
- s++;
- }
- if (*s == '\0') message(-5, "unexpected end of search text");
- *s = '\0';
- if (*p22->sSearch == '\0') /* yes: no search text, do just one find */
- p22->iMaxfind = 1;
-
- p22->sNew = ++s;
- while (*s != '\0' && *s != '\?')
- { if (*s == '\\') s++;
- s++;
- }
- if (*s == '\0') message(-5, "unexpected end of new text");
- *s = '\0';
-
- if (fignorecase) strupr(p22->sSearch); /* convert to uppercase */
- p22->cSearch = cctos(p22->sSearch); /* convert text */
- p22->cNew = cctos(p22->sNew);
- p22->lOffset -= p22->cSearch; /* move back to start of search text */
- }
- /*---------------------- END cctos/parse_patch -----------------------------*/
-
-
- /*------------------------ bios_putc/showhex -------------------------------*/
- /* write unformatted character & attribute and move cursor */
- static void bios_putba(byte *s, int len, int attr)
- {
- union REGS regR;
- int c, r;
-
- regR.h.ah = 3; regR.h.bh = 0; /* get cursor position */
- int86(0x10, ®R, ®R);
- c = regR.h.dl; r = regR.h.dh; /* cursor position */
-
- while (len-- > 0)
- { regR.h.ah = 9; regR.h.bh = 0; /* write character */
- regR.x.cx = 1; regR.h.al = (int)*s++; regR.h.bl = attr;
- int86(0x10, ®R, ®R);
-
- regR.h.ah = 2; regR.h.dl = ++c; regR.h.dh = r;/* move cursor left */
- int86(0x10, ®R, ®R);
- }
- }
- void showhex(byte *sHex, int cHex, long fpos, int himin, int himax)
- {
- byte *s; int j, i, skipl, cDone; char sx[5];
-
- if (cHex == 0) printf("\n");
-
- skipl = (fpos % 16);
- for (cDone = 0; cDone < cHex; cDone = j, sHex = s, fpos += 16)
- { printf("\t%06lx ", fpos - skipl);
-
- bios_putba(s16dots3, 3 * skipl, iColor_lo);
- for (i = skipl, s = sHex, j = cDone; j < cHex && i < 16; i++, s++, j++)
- { sprintf(sx,"%02x ", (int)*s);
- if (j >= himin && j <= himax) bios_putba(sx, 3, iColor_hi);
- else bios_putba(sx, 3, iColor_lo);
- }
- bios_putba(s16dots3, 3 * (16 - i), iColor_lo);
-
- bios_putba(s16dots, skipl, iColor_lo);
- i = skipl;
- for (i = skipl, s = sHex, j = cDone; j < cHex && i < 16; i++, s++, j++)
- { if (j >= himin && j <= himax) bios_putba(s, 1, iColor_hi);
- else bios_putba(s, 1, iColor_lo);
- }
- bios_putba(s16dots, 16 - i, iColor_lo);
- printf("\n");
- skipl = 0;
- }
- }
- /*------------------------ END bios_putc/showhex ---------------------------*/
-
-
- /*------------------- ffindf?/fpatchf --------------------------------------
- * ffindfn locates the next occurence of s in file f
- * the file position is one after the string
- * returns 0 if the string is found, or EOF
- * ffindfi is case insensitive version. It assumes that the search text has
- * been converted to uppercase.
- *--------------------------------------------------------------------------*/
- int ffindfn(FILE *f, byte *s, int slen)
- {
- word ch; int s1len; byte *s1;
-
- if (slen == 0 || (ch = getc(f)) == EOF) return(0);
-
- for (;;)
- {
- s1 = s; s1len = slen - 1;
- while (ch != *s1) /* ch already contains a byte */
- if ( (ch = getc(f)) == EOF) /* find first matching byte or EOF */
- return(EOF);
- if (s1len > 0)
- do
- { if ( (ch = getc(f)) == EOF)
- return(EOF);
- } while (ch == *++s1 && --s1len > 0);
-
- if (s1len == 0)
- return(0);
- else if (slen - s1len - 1 > 0) /* pattern can match in itself */
- { /* slen - s1len + 1 is # of chars matched before failure */
- fseek(f, (long)(1 - slen + s1len), SEEK_CUR);
- ch = *s; /* already matched first character */
- }
- }
- }
- int ffindfi(FILE *f, byte *s, int slen)
- {
- word ch; int s1len; byte *s1;
-
- if (slen == 0 || (ch = getc(f)) == EOF) return(0);
-
- for (;;)
- {
- s1 = s; s1len = slen - 1;
- while (ch != *s1 && toupper(ch) != *s1)/*ch already contains a byte */
- if ( (ch = getc(f)) == EOF) /* find first matching byte or EOF */
- return(EOF);
- if (s1len > 0)
- do
- { if ( (ch = getc(f)) == EOF)
- return(EOF);
- } while ( (ch == *++s1 || toupper(ch) == *s1) && --s1len > 0);
-
- if (s1len == 0)
- return(0);
- else if (slen - s1len - 1 > 0) /* pattern can match in itself */
- { /* slen - s1len + 1 is # of chars matched before failure */
- fseek(f, (long)(1 - slen + s1len), SEEK_CUR);
- ch = *s; /* already matched first character */
- }
- }
- }
- void fpatchf(FILE *f, char *fname, struct Patch22 *p22)
- {
- int key = 'Y'; long fpos, wpos;
- int i, ch, start, end, cFind;
- static byte sOld[MAXPATLEN + 50];
-
- cFind = p22->cPatch = 0;
- while (p22->iMaxpatch != 0 && p22->iMaxfind != 0
- && (* ffindf)(f, p22->sSearch, p22->cSearch) == 0)
- { cFind++; p22->iMaxfind--;
- fpos = ftell(f); /* now we are at first byte after search text */
- if (fVerbose_)
- { printf("Search:");
- showhex(p22->sSearch, p22->cSearch, fpos - p22->cSearch,
- 0, p22->cSearch);
- }
- if (fseek(f, p22->lOffset, SEEK_CUR) == 0)
- { /* go to write position: start of search + user defined offset */
- if (fVerbose_)
- { wpos = ftell(f); /* write position */
- start = - (wpos % 16);/*context upto previous multiple of 16*/
- if (wpos > 15) start -= 16;/* one previous line of context */
- fseek(f, (long)start, SEEK_CUR);
- end = p22->cNew - start;
- end += 32 - (end % 16); /* context: upto eol + extra line */
- for (i = 0; i < end && (ch = getc(f)) != EOF; i++)
- sOld[i] = ch;
- fseek(f, wpos, SEEK_SET); /* back to write position */
- printf("Old:");
- showhex(sOld, i, wpos + start, - start, p22->cNew - start - 1);
-
- printf("New:");
- showhex(p22->sNew, p22->cNew, fpos + p22->lOffset, 0,p22->cNew);
- printf("Change [Y/N/Q]? ");
- while ( (key = toupper(getch())) != 'N' && key != 'Y'
- && key != 'Q') ;
- printf("%c", key);
-
- if (key == 'N') printf(" - NOT patched.\n");
- else if (key == 'Q')
- { printf(" - Quitting: no further patches.\n");
- break; /* break out of while loop */
- }
- }
-
- if (key == 'Y')
- { for (i = 0; i < p22->cNew; i++) putc(p22->sNew[i], f) ;
- p22->cPatch++; p22->iMaxpatch--;
- if (fVerbose_) printf(" - patched.\n");
- }
- }
- /* move one byte beyond start of match */
- fseek(f, fpos - p22->cSearch + 1, SEEK_SET);
- }
- if (fVerbose_ && cFind == 0)
- { printf("Search:");
- showhex(p22->sSearch, p22->cSearch, 0L, 0, p22->cSearch);
- printf("Not found.\n");
- }
-
- printf("\n%d Patche(s) made to %s by %s.\n", p22->cPatch, fname, sProgram);
-
- free(sOld);
- }
- /*------------------- END ffindf?/fpatchf ----------------------------------*/
-
-
- /*========================== main ==========================================*/
- main(int argc, char *argv[])
- {
- static char infname[80]; /* name if file to be patched */
- static char comfname[80]; /* name if file with patch commands */
- static byte sPatch[MAXPATLEN]; /* buffer to hold patch */
- static struct Patch22 p22; /* holds patch information */
- int npatches = 0;
-
- infname[0] = comfname[0] = '\0';
-
- if (*sPassword)
- { --argc; ++argv; /* first argument must be password */
- if (strcmp(sPassword, *argv) != 0) return(0);
- }
- if (--argc < 1 || **++argv == '?') ERR_EXIT;
- while (argc > 0)
- { if (**argv == '@')
- { strcpy(comfname, ++*argv);
- }
- else
- { if (infname[0] == '\0')
- strcpy(infname, *argv); /* no file yet, file name */
- else
- strcpy(sPatch, *argv); /* no search string yet */
- } /* first letter of switch */
- --argc; ++argv;
- } /* end of exploration of command line arguments */
-
- if (infname[0] != '\0') /* yes: commands from command line */
- { if (!open_infile(infname)) exit(-3); /* read & write, binary */
- parse_patch(sPatch, &p22); /* parse the command string */
- fpatchf(infile_, infname, &p22);
- close_infile(p22.cPatch);
- npatches = p22.cPatch;
- }
- else if (comfname[0] != '\0') /* yes: commands from command file */
- { if (!open_comfile(comfname)) exit(-4); /* read only, not binary */
-
- while (getpatch(comfile_, infname, sPatch) == 0)
- { if (!open_infile(infname)) continue; /* read & write, binary */
- parse_patch(sPatch, &p22); /* parse the command string */
- fpatchf(infile_, infname, &p22);
- close_infile(p22.cPatch);
- npatches += p22.cPatch;
- }
- fclose(comfile_);
- }
- else
- message(-2, "Invalid command line: nothing to do");
-
- return(npatches);
- }
-