home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / FILUTL / P22V12.ZIP / PATCH22.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-08-08  |  23.5 KB  |  570 lines

  1. #include <conio.h>
  2. #include <dos.h>
  3. #include <ctype.h>
  4. #undef tolower             /* use function version instead of macro version */
  5. #undef toupper
  6. #include <stdarg.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10.  
  11. /*------------------------- PATCH22 ---------------------------------------
  12.  * just compile as: cl patch22.c
  13.  *
  14.  * exit value (byte):
  15.  *   -5: error in patch command string
  16.  *   -4: file with patch commands not found
  17.  *   -3: file to be patched not found
  18.  *   -2: error in patch commandline (if not silent help is displayed)
  19.  *   -1: not used (reserved for calling through spawn functions)
  20.  * >= 0: total no of patches made
  21.  *-------------------------------------------------------------------------*/
  22.  
  23. /*----------------------------- Revision history ---------------------------
  24.  *  8-08-91: Released version 1.2
  25.  * 22-07-91: - The date/time stamp of a file is reset if any patches are made.
  26.  *           - Added (simple) password protection option.
  27.  *  7-07-91: - If user chooces Quit, now breaks out of while loop, instead
  28.  *           of spooling to end of file.
  29.  *           - Corrected bug pointed out by Ernst Raedecker of Amsterdam:
  30.  *           find function did not allow for search string matching within
  31.  *           itself. (See patch22.doc)
  32.  *           - Now the next search continues one byte after the start of the
  33.  *           previous match.
  34.  * 12-05-91: cctos() converts character following \ to lowercase, changed
  35.  *           ffindi() slightly.
  36.  * 11-05-91: Now checks for progress in conditions field (+A000 led to
  37.  *           infinite loop). Added case insensitive option.
  38.  * 11-04-91: Added <Q>uit option to verbose patching.
  39.  *  5-04-91: Replaced function Abort by message (using stdarg).
  40.  *           Now continues with next patch if taking patches from file and
  41.  *           file to be patched not found.
  42.  * 28-02-91: Fixed bug in ffindf function (the first non-matching char after
  43.  *           a match was not used as the starting char in the next search).
  44.  * 22-02-91: Added error message if 0 patches requested.
  45.  * 18-02-91: Command line patch didn't work, bug fixed.
  46.  * 03-02-91: Added \w char escape codes, replaced mallocs by static arrays,
  47.  *           using scanf to read \x##, \w####, \### (see NB.2 in cctos).
  48.  * 02-02-91: Less silent; removed error in scanning upto ?; added missing
  49.  *           breaks in switching for \ char escape codes.
  50.  *--------------------------------------------------------------------------*/
  51.  
  52.  
  53. /*=========================== global help ==================================*/
  54. #define ERR_EXIT  {  fprintf(stderr, sUsage, sProgram, sVersion, sAuthor, \
  55.     sProgram, sProgram, sProgram, sProgram);  exit(0); }
  56.  
  57. char *sProgram = "PATCH22";
  58. char *sVersion = "version 1.2";
  59. char *sAuthor = "(C) J.A. Doornik,  8 August 1991.";
  60.  
  61. char  sP22[] = { '«','p','2','2','_','»' };                    /* signature */
  62. char  sPassword[] = "\0 gf ggv aewf bvsbago";      /* password: 21 chars+\0 */
  63.                 /* leading \0 indicates no password; avoid .exe compression */
  64.  
  65. char *sUsage =
  66.     "\n%s %s: patches any file %s\n"
  67.     "\nUsage:\t%s fname  +##*##si\?search_text\?new_text\?"
  68.     "\n   or:\t%s @fname\n"
  69.     "\nfname\tfile to be patched"
  70.     "\n+##\t+ve or -ve offset from the start of the search text"
  71.     "\n*##\tmaximum number of patches"
  72.     "\ns\tsilent patch, default is confirm each patch"
  73.     "\ni\tcase insensitive patch, default is case sensitive"
  74.     "\n@fname\tfile with (multiple) %s commands, with format:"
  75.     "\n\tfname  +##*##s\?search_text\?new_text\?"
  76.     "\ntext\tC-type character constant, possibly including:"
  77.     "\n\t\\a (07h), \\b (08h), \\f (0Ch), \\n (0Ah), \\r (0Dh), \\t (09h), \\v (0Bh);"
  78.     "\n\t\\###   (octal byte, not more than 3 digits);"
  79.     "\n\t\\w#### (hexadecimal word, not more than 4 digits);"
  80.     "\n\t\\x##   (hexadecimal byte, not more than 2 digits);"
  81.     "\n\t\\h     (the rest of the text contains two-digit hexadecimal bytes)."
  82.     "\n\tThe characters \\ \? \' \" must be written as: \\\" \\\\ \\\? \\\' \\\"."
  83.     "\n\nEg.\t%s test *1\?\h0d0a00\?\\r\\n\\0\?"
  84.     "\nwhich makes one change, where the replace text equals the search text.\n";
  85.  
  86.  
  87. /*=========================== global types =================================*/
  88. typedef unsigned char   byte;       /* b   */
  89. typedef unsigned short  bool;       /* f   */
  90. typedef unsigned short  word;       /* w   */
  91.  
  92. #define  FALSE  0
  93. #define  TRUE   !FALSE
  94.  
  95. struct Patch22                         /* structure to store all patch info */
  96. {
  97.     byte *sSearch, *sNew;                       /* search text and new text */
  98.     int  cNew, cSearch;           /* # of bytes in search text and new text */
  99.     int  iMaxpatch, cPatch;         /* maximum and actual number of patches */
  100.     int  iMaxfind;                               /* maximum number of finds */
  101.     long lOffset;                     /* offset from start of search string */
  102. };
  103.  
  104. /*=========================== global data ==================================*/
  105. #define MAXPATLEN 1024                    /* maximum length of a patch text */
  106. static bool  fVerbose_ = TRUE;
  107. static int   iColor_hi = 0x001f, iColor_lo = 0x0017;
  108. static char *s16dots =  "∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙";
  109. static char *s16dots3 = "∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ∙∙ ";
  110.  
  111. int  ffindfn(FILE *f, byte *s, int slen);                     /* prototypes */
  112. int  ffindfi(FILE *f, byte *s, int slen);
  113. int  (* ffindf)(FILE *, byte *, int) = ffindfn;      /* case sensitive find */
  114.  
  115. /* --------------- files and  internal buffer for in/output --------------- */
  116. #define BUFLEN 20480                                          /* 20K buffer */
  117. static byte  inbuf_[BUFLEN];
  118. static FILE  *infile_, *comfile_;             /* files are global variables */
  119. unsigned uFdate, uFtime;             /* date and time of file to be patched */
  120.  
  121. /*--------------------------- procedure message ----------------------------*/
  122. void message(int exitcode, char *msg, ...)
  123. {
  124.     va_list args;
  125.  
  126.     fprintf(stderr, "%s error: ", sProgram);
  127.     va_start(args, msg);    vfprintf(stderr, msg, args);    va_end(args);
  128.     fprintf(stderr, ".\n");
  129.  
  130.     if (exitcode != 0)  exit(exitcode);
  131. }
  132. /*--------------------------- end procedure message ------------------------*/
  133.  
  134.  
  135. /*------------------ open_infile/close_infile/open_comfile -----------------*/
  136. int  open_infile(char *infname)                   /* get file to be patched */
  137. {
  138.     if ( (infile_ = fopen(infname, "r+b")) == NULL) /* read & write, binary */
  139.     {   message(0, "cannot open file %s", infname);
  140.         return(FALSE);
  141.     }
  142.     _dos_getftime(fileno(infile_), &uFdate, &uFtime); /* get file date/time */
  143.     setvbuf(infile_, inbuf_, _IOFBF, BUFLEN);
  144. return(TRUE);
  145. }
  146.  
  147. void close_infile(int npatches)                       /* close patched file */
  148. {
  149.     if (npatches > 0)
  150.         _dos_setftime(fileno(infile_), uFdate, uFtime);  /* reset file date */
  151.     fclose(infile_);
  152. }
  153.  
  154. int  open_comfile(char *comfname)           /* get file with patch commands */
  155. {
  156.     if ( (comfile_ = fopen(comfname, "r")) == NULL)                 /* read */
  157.     {   message(0, "cannot open file %s", comfname);
  158.         return(FALSE);
  159.     }
  160. return(TRUE);
  161. }
  162. /*------------------ END open_infile/close_infile/open_comfile -------------*/
  163.  
  164.  
  165. /*------------------------------- getpatch ---------------------------------*/
  166. int   getpatch(FILE *f, char *infname, byte *s)
  167. {                   /* gets fname +##*##s?search_text?new_text? from a file */
  168.     int  ch, len;
  169.  
  170.     do
  171.     {   fscanf(f, " ");                                 /* skip white space */
  172.         ch = 0;  fscanf(f, ";%n", &ch);            /* check if there is a ; */
  173.         if (ch > 0)  fscanf(f, "%*[^\n]");    /* skip comment lines upto \n */
  174.     } while (!feof(f) && ch > 0);
  175.     if (fscanf(f, " %s ", infname) != 1)  return(EOF);
  176.  
  177.     len = MAXPATLEN - 1;                      /* read upto first question mark */
  178.     while (--len > 0 && (ch = fgetc(f)) != EOF && ch != '\?')
  179.         if (ch != '\n') *s++ = ch;
  180.     *s++ = '\?';
  181.     len = MAXPATLEN - 1;                     /* read upto second question mark */
  182.     while (--len > 0 && (ch = fgetc(f)) != EOF && ch != '\?')
  183.     {   if (ch == '\\')
  184.         {   *s++ = ch;  *s++ = fgetc(f);  --len;  /* read char. escape code */
  185.         }
  186.         else if (ch != '\n') *s++ = ch;
  187.     }
  188.     *s++ = '\?';
  189.     len = MAXPATLEN - 1;                   /* read upto third question mark */
  190.     while (--len > 0 && (ch = fgetc(f)) != EOF && ch != '\?')
  191.     {   if (ch == '\\')
  192.         {   *s++ = ch;  *s++ = fgetc(f);  --len;  /* read char. escape code */
  193.         }
  194.         else if (ch != '\n') *s++ = ch;
  195.     }
  196.     *s++ = '\?';
  197.     *s = '\0';
  198.  
  199.     if (ch == EOF)  message(-5, "unexpected end of command file");
  200. return(0);
  201. }
  202. /*------------------------------- END getpatch -----------------------------*/
  203.  
  204.  
  205. /*---------------------- cctos/parse_patch ---------------------------------
  206.  * NB.1 for octal and hexadecimal escape sequences:
  207.  *     - octal can not be more than 3 digits;
  208.  *     - hexadecimal can not be more than 2 digits;
  209.  *       (C 6.0: hexadecimal escape sequences use every potential hexadecimal
  210.  *               digit following the \x, so "\x005float" is "_loat".)
  211.  * NB.2 Usage of %n format in scanf:
  212.  *      Consider reading: num = 0; sscanf(cc, "%3o%n", &ch, &num);
  213.  *      where cc = "01"
  214.  *      Then scanning stops at the '\0', before the %n is reached, and the
  215.  *      num argument is NOT set. This seems a compiler bug.
  216.  *      If no characters are read, num is not set to zero.
  217.  *--------------------------------------------------------------------------*/
  218. static int   cctos(byte *cc)        /* convert character constant to string */
  219. {
  220. static byte src[MAXPATLEN + 5];
  221.     byte *s;  int  len, num;  unsigned int ch;
  222.  
  223.     strcpy(src, cc);
  224.     s = cc;  cc = src; /* input from static src, output overwrites argument */
  225.     strcat(cc, "\a\a\a\a");     /* to avoid problems as pointed out in NB.2 */
  226.            /* the entry string is a character string which does not contain */
  227.                       /* chars < 32 (except '\0'), use '\a' to indicate end */
  228.  
  229.     for (len = 0; *cc != '\a'; cc++, s++, len++)            /* scan upto \a */
  230.     {   if(*cc != '\\')
  231.             *s = *cc;
  232.         else
  233.         {   switch (tolower(*++cc))
  234.             {   case 'a':  *s = '\a';  break;
  235.                 case 'b':  *s = '\b';  break;
  236.                 case 'f':  *s = '\f';  break;
  237.                 case 'n':  *s = '\n';  break;
  238.                 case 'r':  *s = '\r';  break;
  239.                 case 't':  *s = '\t';  break;
  240.                 case 'v':  *s = '\v';  break;
  241.                 case '\\': *s = '\\';  break;
  242.                 case '\?': *s = '\?';  break;
  243.                 case '\'': *s = '\'';  break;
  244.                 case '\"': *s = '\"';  break;
  245.                 case 'h':
  246.                     for (++cc; *cc != '\a'; cc += 2, s++, len++)
  247.                     {   num = 0;  sscanf(cc, "%2x%n", &ch, &num);
  248.                         *s = (byte) ch;
  249.                         if (num != 2)
  250.                             message(-5, "Error in \\h escape sequence: %s", cc);
  251.                     }
  252.                     s--;  len--;
  253.                     break;
  254.                 case 'w':                              /* hexadecimal word */
  255.                     num = 0;  sscanf(++cc, "%4x%n", (unsigned int *)s, &num);
  256.                     s++;  len++;                     /* read word, not byte */
  257.                     if (num == 0)
  258.                         message(-5, "Error in \\w escape sequence: %s", cc - 2);
  259.                     cc += num - 1;
  260.                     break;
  261.                 case 'x':                               /* hexadecimal byte */
  262.                     num = 0;  sscanf(++cc, "%2x%n", &ch, &num);
  263.                     *s = (byte) ch;
  264.                     if (num == 0)
  265.                         message(-5, "Error in \\x escape sequence: %s", cc - 2);
  266.                     cc += num - 1;
  267.                     break;
  268.                 default:           /* read octal byte, else error in string */
  269.                     num = 0;  sscanf(cc, "%3o%n", &ch, &num);
  270.                     *s = (byte) ch;
  271.                     if (num == 0)
  272.                         message(-5, "Unknown escape sequence: %s", cc - 1);
  273.                     cc += num - 1;
  274.             }
  275.         }
  276.     }
  277. return(len);
  278. }
  279. void  parse_patch(byte *s, struct Patch22 *p22)
  280. {                                    /* parse +##*##s?search_text?new_text? */
  281.     byte *s1;  bool fignorecase = FALSE;
  282.  
  283.     fVerbose_ = TRUE;
  284.     p22->cNew = p22->cSearch = 0;
  285.     p22->iMaxpatch = p22->iMaxfind = -1;  p22->lOffset = 0L;
  286.  
  287.     while (*s != '\0' && *s != '\?')                   /* read upto first ? */
  288.     {   if (*s == '+' || *s == '-' || isdigit(*s))
  289.             p22->lOffset = strtol(s, &s1, 0);
  290.         else if (*s == '*')
  291.         {   if ( (p22->iMaxpatch = (int) strtol(s + 1, &s1, 0)) == 0)
  292.                 message(-5, "0 patches requested");
  293.         }
  294.         else if (*s == 's' || *s == 'S')
  295.         {   fVerbose_ = FALSE;
  296.             for (s1 = s + 1; isspace(*s1); s1++) ;
  297.         }
  298.         else if (*s == 'i' || *s == 'I')
  299.         {   ffindf = ffindfi;                    /* case insensitive search */
  300.             fignorecase = TRUE;
  301.             for (s1 = s + 1; isspace(*s1); s1++) ;
  302.         }
  303.         else if (*s != '\?')
  304.             message(-5, "\? expected");
  305.  
  306.         if (s1 == s) /* now s1 points to next pos, check if not same as old */
  307.             message(-5, "unable to scan conditions field");
  308.         s = s1;
  309.     }
  310.  
  311.     p22->sSearch = ++s;                                     /* skip first ? */
  312.     while (*s != '\0' && *s != '\?')
  313.     {   if (*s == '\\')  s++;
  314.         s++;
  315.     }
  316.     if (*s == '\0')  message(-5, "unexpected end of search text");
  317.     *s = '\0';
  318.     if (*p22->sSearch == '\0')     /* yes: no search text, do just one find */
  319.         p22->iMaxfind = 1;
  320.  
  321.     p22->sNew = ++s;
  322.     while (*s != '\0' && *s != '\?')
  323.     {   if (*s == '\\')  s++;
  324.         s++;
  325.     }
  326.     if (*s == '\0')  message(-5, "unexpected end of new text");
  327.     *s = '\0';
  328.  
  329.     if (fignorecase)  strupr(p22->sSearch);         /* convert to uppercase */
  330.     p22->cSearch = cctos(p22->sSearch);                     /* convert text */
  331.     p22->cNew = cctos(p22->sNew);        
  332.     p22->lOffset -= p22->cSearch;      /* move back to start of search text */
  333. }
  334. /*---------------------- END cctos/parse_patch -----------------------------*/
  335.  
  336.  
  337. /*------------------------ bios_putc/showhex -------------------------------*/
  338.                  /* write unformatted character & attribute and move cursor */
  339. static void bios_putba(byte *s, int len, int attr)
  340. {
  341.     union REGS regR;
  342.     int  c, r;
  343.  
  344.     regR.h.ah = 3;  regR.h.bh = 0;                   /* get cursor position */
  345.     int86(0x10, ®R, ®R);
  346.     c = regR.h.dl;  r = regR.h.dh;                       /* cursor position */
  347.  
  348.     while (len-- > 0)
  349.     {   regR.h.ah = 9;  regR.h.bh = 0;                       /* write character */
  350.         regR.x.cx = 1;  regR.h.al = (int)*s++;  regR.h.bl = attr;
  351.         int86(0x10, ®R, ®R);
  352.  
  353.         regR.h.ah = 2;  regR.h.dl = ++c;  regR.h.dh = r;/* move cursor left */
  354.         int86(0x10, ®R, ®R);
  355.     }
  356. }
  357. void showhex(byte *sHex, int cHex, long fpos, int himin, int himax)
  358. {
  359.     byte *s;  int j, i, skipl, cDone;  char sx[5];
  360.  
  361.     if (cHex == 0)  printf("\n");
  362.  
  363.     skipl = (fpos % 16);
  364.     for (cDone = 0; cDone < cHex; cDone = j, sHex = s, fpos += 16)
  365.     {   printf("\t%06lx  ", fpos - skipl);
  366.  
  367.         bios_putba(s16dots3, 3 * skipl, iColor_lo);
  368.         for (i = skipl, s = sHex, j = cDone; j < cHex && i < 16; i++, s++, j++)
  369.         {   sprintf(sx,"%02x ", (int)*s);
  370.             if (j >= himin && j <= himax)  bios_putba(sx, 3, iColor_hi);
  371.             else                           bios_putba(sx, 3, iColor_lo);
  372.         }
  373.         bios_putba(s16dots3, 3 * (16 - i), iColor_lo);
  374.  
  375.         bios_putba(s16dots, skipl, iColor_lo);
  376.         i = skipl;
  377.         for (i = skipl, s = sHex, j = cDone; j < cHex && i < 16; i++, s++, j++)
  378.         {   if (j >= himin && j <= himax) bios_putba(s, 1, iColor_hi);
  379.             else                          bios_putba(s, 1, iColor_lo);
  380.         }
  381.         bios_putba(s16dots, 16 - i, iColor_lo);
  382.         printf("\n");
  383.         skipl = 0;
  384.     }
  385. }
  386. /*------------------------ END bios_putc/showhex ---------------------------*/
  387.  
  388.  
  389. /*------------------- ffindf?/fpatchf --------------------------------------
  390.  * ffindfn locates the next occurence of s in file f
  391.  *   the file position is one after the string
  392.  *   returns 0 if the string is found, or EOF
  393.  * ffindfi is case insensitive version. It assumes that the search text has
  394.  *   been converted to uppercase.
  395.  *--------------------------------------------------------------------------*/
  396. int  ffindfn(FILE *f, byte *s, int slen)
  397. {
  398.     word ch;  int s1len;  byte *s1;
  399.  
  400.     if (slen == 0 || (ch = getc(f)) == EOF)  return(0);
  401.  
  402.     for (;;)
  403.     {
  404.         s1 = s;  s1len = slen - 1;
  405.         while (ch != *s1)                     /* ch already contains a byte */
  406.             if ( (ch = getc(f)) == EOF)  /* find first matching byte or EOF */
  407.                 return(EOF);
  408.         if (s1len > 0)
  409.             do
  410.             {   if ( (ch = getc(f)) == EOF)
  411.                     return(EOF);
  412.             } while (ch == *++s1 && --s1len > 0);
  413.  
  414.         if (s1len == 0)
  415.            return(0);
  416.         else if (slen - s1len - 1 > 0)       /* pattern can match in itself */
  417.         {          /* slen - s1len + 1 is # of chars matched before failure */
  418.             fseek(f, (long)(1 - slen + s1len), SEEK_CUR);
  419.             ch = *s;                     /* already matched first character */
  420.         }
  421.     }
  422. }
  423. int  ffindfi(FILE *f, byte *s, int slen)
  424. {
  425.     word ch;  int s1len;  byte *s1;
  426.  
  427.     if (slen == 0 || (ch = getc(f)) == EOF)  return(0);
  428.  
  429.     for (;;)
  430.     {
  431.         s1 = s;  s1len = slen - 1;
  432.         while (ch != *s1 && toupper(ch) != *s1)/*ch already contains a byte */
  433.             if ( (ch = getc(f)) == EOF)  /* find first matching byte or EOF */
  434.                 return(EOF);
  435.         if (s1len > 0)
  436.             do
  437.             {   if ( (ch = getc(f)) == EOF)
  438.                     return(EOF);
  439.             } while ( (ch == *++s1 || toupper(ch) == *s1) && --s1len > 0);
  440.  
  441.         if (s1len == 0)
  442.            return(0);
  443.         else if (slen - s1len - 1 > 0)       /* pattern can match in itself */
  444.         {          /* slen - s1len + 1 is # of chars matched before failure */
  445.             fseek(f, (long)(1 - slen + s1len), SEEK_CUR);
  446.             ch = *s;                     /* already matched first character */
  447.         }
  448.     }
  449. }
  450. void fpatchf(FILE *f, char *fname, struct Patch22 *p22)
  451. {
  452.     int  key = 'Y';  long fpos, wpos;
  453.     int  i, ch, start, end, cFind;
  454. static byte sOld[MAXPATLEN + 50];
  455.  
  456.     cFind = p22->cPatch = 0;
  457.     while (p22->iMaxpatch != 0 && p22->iMaxfind != 0
  458.            && (* ffindf)(f, p22->sSearch, p22->cSearch) == 0)
  459.     {   cFind++;  p22->iMaxfind--;
  460.         fpos = ftell(f);      /* now we are at first byte after search text */
  461.         if (fVerbose_)
  462.         {   printf("Search:");
  463.             showhex(p22->sSearch, p22->cSearch, fpos - p22->cSearch,
  464.                 0, p22->cSearch);
  465.         }
  466.         if (fseek(f, p22->lOffset, SEEK_CUR) == 0)
  467.         {    /* go to write position: start of search + user defined offset */
  468.             if (fVerbose_)
  469.             {   wpos = ftell(f);                          /* write position */
  470.                 start = - (wpos % 16);/*context upto previous multiple of 16*/
  471.                 if (wpos > 15)  start -= 16;/* one previous line of context */
  472.                 fseek(f, (long)start, SEEK_CUR);
  473.                 end = p22->cNew - start;
  474.                 end += 32 - (end % 16);   /* context: upto eol + extra line */
  475.                 for (i = 0; i < end && (ch = getc(f)) != EOF; i++)
  476.                     sOld[i] = ch;
  477.                 fseek(f, wpos, SEEK_SET);         /* back to write position */
  478.                 printf("Old:");
  479.                 showhex(sOld, i, wpos + start, - start, p22->cNew - start - 1);
  480.  
  481.                 printf("New:");
  482.                 showhex(p22->sNew, p22->cNew, fpos + p22->lOffset, 0,p22->cNew);
  483.                 printf("Change [Y/N/Q]? ");
  484.                 while ( (key = toupper(getch())) != 'N' && key != 'Y'
  485.                         && key != 'Q') ;
  486.                 printf("%c", key);
  487.  
  488.                 if (key == 'N')  printf(" - NOT patched.\n");
  489.                 else if (key == 'Q')
  490.                 {   printf(" - Quitting: no further patches.\n");
  491.                     break;                       /* break out of while loop */
  492.                 }
  493.             }
  494.  
  495.             if (key == 'Y')
  496.             {   for (i = 0; i < p22->cNew; i++)  putc(p22->sNew[i], f) ;
  497.                 p22->cPatch++;  p22->iMaxpatch--;
  498.                 if (fVerbose_)  printf(" - patched.\n");
  499.             }
  500.         }
  501.                                      /* move one byte beyond start of match */
  502.         fseek(f, fpos - p22->cSearch + 1, SEEK_SET);
  503.     }
  504.     if (fVerbose_ && cFind == 0)
  505.     {   printf("Search:");
  506.         showhex(p22->sSearch, p22->cSearch, 0L, 0, p22->cSearch);
  507.         printf("Not found.\n");
  508.     }
  509.  
  510.     printf("\n%d Patche(s) made to %s by %s.\n", p22->cPatch, fname, sProgram);
  511.  
  512.     free(sOld);
  513. }
  514. /*------------------- END ffindf?/fpatchf ----------------------------------*/
  515.  
  516.  
  517. /*========================== main ==========================================*/
  518. main(int argc, char *argv[])
  519. {
  520. static char infname[80];                      /* name if file to be patched */
  521. static char comfname[80];               /* name if file with patch commands */
  522. static byte sPatch[MAXPATLEN];                      /* buffer to hold patch */
  523. static struct Patch22 p22;                       /* holds patch information */
  524.     int npatches = 0;
  525.  
  526.     infname[0] = comfname[0] = '\0';
  527.  
  528.     if (*sPassword)
  529.     {   --argc;  ++argv;                 /* first argument must be password */
  530.         if (strcmp(sPassword, *argv) != 0)  return(0);
  531.     }
  532.     if (--argc < 1 || **++argv == '?')  ERR_EXIT;
  533.     while (argc > 0)
  534.     {   if (**argv == '@')
  535.         {   strcpy(comfname, ++*argv);
  536.         }
  537.         else
  538.         {   if (infname[0] == '\0')
  539.                 strcpy(infname, *argv);           /* no file yet, file name */
  540.             else
  541.                 strcpy(sPatch, *argv);              /* no search string yet */
  542.         } /* first letter of switch */
  543.     --argc;  ++argv;
  544.     }                       /* end of exploration of command line arguments */
  545.  
  546.     if (infname[0] != '\0')              /* yes: commands from command line */
  547.     {   if (!open_infile(infname))  exit(-3);         /* read & write, binary */
  548.         parse_patch(sPatch, &p22);              /* parse the command string */
  549.         fpatchf(infile_, infname, &p22);
  550.         close_infile(p22.cPatch);
  551.         npatches = p22.cPatch;
  552.     }
  553.     else if (comfname[0] != '\0')        /* yes: commands from command file */
  554.     {   if (!open_comfile(comfname))  exit(-4);      /* read only, not binary */
  555.  
  556.         while (getpatch(comfile_, infname, sPatch) == 0)
  557.         {   if (!open_infile(infname))  continue;     /* read & write, binary */
  558.             parse_patch(sPatch, &p22);          /* parse the command string */
  559.             fpatchf(infile_, infname, &p22);
  560.             close_infile(p22.cPatch);
  561.             npatches += p22.cPatch;
  562.         }
  563.         fclose(comfile_);
  564.     }
  565.     else
  566.         message(-2, "Invalid command line: nothing to do");
  567.  
  568. return(npatches);
  569. }
  570.