home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / os2sdk / os2sdk12 / cpgrep / cpgrep.c next >
Encoding:
C/C++ Source or Header  |  1989-11-20  |  28.9 KB  |  995 lines

  1. /* cpgrep - string searches
  2.  *
  3.  * Created by Microsoft Corp. 1986
  4.  *
  5.  *
  6.  */
  7.  
  8. #include        <os2def.h>
  9. #define INCL_DOSPROCESS
  10. #define INCL_DOSSEMAPHORES
  11. #define INCL_DOSQUEUES
  12. #define INCL_DOSMEMMGR
  13. #define INCL_DOSMISC
  14. #include        <bsedos.h>
  15. #include        <stdio.h>
  16. #include        <fcntl.h>
  17. #include        <ctype.h>
  18.  
  19. #define    BEGLINE        0x40
  20. #define    DEBUG        0x08
  21. #define    ENDLINE        0x80
  22. #define    FILBUFLEN    (SECTORLEN*30)
  23. #define    FILNAMLEN    80
  24. #define    INVERT        0x10
  25. #define    ISCOT        0x0002
  26. #define    LG2SECLEN    9
  27. #define    LINELEN        128
  28. #define    LINENOS        0x04
  29. #define    LNOLEN        8
  30. #define    MAXSTRLEN    128
  31. #define    NAMEONLY    0x02
  32. #define    OUTBUFLEN    (SECTORLEN*4)
  33. #define    SECTORLEN    (1 << LG2SECLEN)
  34. #define    SHOWNAME    0x01
  35. #define    STKLEN        256
  36. #define    TIMER        0x20
  37. #define    TRTABLEN    256
  38. #define    s_text(x)    (((char *)(x)) - ((x)->s_must))
  39.  
  40. typedef struct stringnode
  41.   {
  42.     struct stringnode    *s_alt;        /* List of alternates */
  43.     struct stringnode    *s_suf;        /* List of suffixes */
  44.     int            s_must;        /* Length of portion that must match */
  45.   }
  46.             STRINGNODE;
  47.  
  48. char            filbuf[(FILBUFLEN + 2)*2];
  49. char            *bufptr[] = { filbuf, filbuf + FILBUFLEN + 2 };
  50. char            outbuf[OUTBUFLEN*2];
  51. char            *obuf[] = { outbuf, outbuf + OUTBUFLEN };
  52. char            *optr[] = { outbuf, outbuf + OUTBUFLEN };
  53. USHORT            ocnt[] = { OUTBUFLEN, OUTBUFLEN };
  54. int            oi = 0;
  55. char            transtab[TRTABLEN] = { 0 };
  56. STRINGNODE        *stringlist[TRTABLEN/2];
  57. USHORT            arrc;        /* I/O return code for DosRead */
  58. USHORT            awrc;        /* I/O return code for DosWrite */
  59. int            casesen = 1;    /* Assume case-sensitivity */
  60. USHORT            cbread;     /* Bytes read by DosRead */
  61. USHORT            cbwrite;    /* Bytes written by DosWrite */
  62. int            clists = 1;    /* One is first available index */
  63. int            flags;        /* Flags */
  64. int            lineno;        /* Current line number */
  65. char            pmode;        /* Protected mode flag */
  66. LONG            readdone;    /* Async read done semaphore */
  67. LONG            readpending;    /* Async read pending semaphore */
  68. int            status = 1;    /* Assume failure */
  69. char            *t2buf;        /* Async read buffer */
  70. USHORT            t2buflen;    /* Async read buffer length */
  71. HFILE            t2fd;        /* Async read file */
  72. char            *t3buf;        /* Async write buffer */
  73. USHORT            t3buflen;    /* Async write buffer length */
  74. HFILE            t3fd;        /* Async write file */
  75. LONG            writedone;    /* Async write done semaphore */
  76. LONG            writepending;    /* Async write pending semaphore */
  77. char            target[MAXSTRLEN];
  78.                     /* Last string added */
  79. int            targetlen;    /* Length of last string added */
  80.  
  81. int            countlines();    /* See CPGREPSB.ASM */
  82. int            countmatched();    /* See CPGREPSB.ASM */
  83. char            *findlist();    /* See CPGREPSB.ASM */
  84. char            *findone();    /* See CPGREPSB.ASM */
  85. void            flush1buf();    /* See below */
  86. int            grepbuffer();    /* See below */
  87. int            revfind();    /* See CPGREPSB.ASM */
  88. void            write1buf();    /* See below */
  89. char            *(*find)() = findlist;
  90. void            (*flush1)() = flush1buf;
  91. int            (*grep)() = grepbuffer;
  92. void            (*write1)() = write1buf;
  93.  
  94. void            freenode(x)
  95. register STRINGNODE    *x;        /* Pointer to node to free */
  96.   {
  97.     register STRINGNODE    *y;        /* Pointer to next node in list */
  98.  
  99.     while(x != NULL)            /* While not at end of list */
  100.       {
  101.     if(x->s_suf != NULL) freenode(x->s_suf);
  102.                     /* Free suffix list */
  103.     y = x;                /* Save pointer */
  104.     x = x->s_alt;            /* Move down the list */
  105.     free(y);            /* Free the node */
  106.       }
  107.   }
  108.  
  109.  
  110. STRINGNODE        *newnode(s,n)
  111. char            *s;        /* String */
  112. int            n;        /* Length of string */
  113.   {
  114.     register STRINGNODE    *new;        /* Pointer to new node */
  115.     char        *t;        /* String pointer */
  116.     char        *malloc();    /* Storage allocator */
  117.  
  118.     if((t = malloc(sizeof(STRINGNODE) + n + (n & 1))) == NULL)
  119.       {                    /* If allocation fails */
  120.     fprintf(stderr,"Out of memory\n");
  121.     DosExit( EXIT_PROCESS, 2);    /* Print error message and die */
  122.       }
  123.     if(n & 1) ++t;            /* END of string word-aligned */
  124.     strncpy(t,s,n);            /* Copy string text */
  125.     new = (STRINGNODE *)(t + n);    /* Set pointer to node */
  126.     new->s_alt = NULL;            /* No alternates yet */
  127.     new->s_suf = NULL;            /* No suffixes yet */
  128.     new->s_must = n;            /* Set string length */
  129.     return(new);            /* Return pointer to new node */
  130.   }
  131.  
  132.  
  133. void            reallocnode(node,s,n)
  134. register STRINGNODE    *node;        /* Pointer to node */
  135. char            *s;        /* String */
  136. register int        n;        /* Length of string */
  137.   {
  138.     if(n > node->s_must)        /* If node must grow */
  139.       {
  140.     fprintf(stderr,"Internal error\n");
  141.                     /* Error message */
  142.     DosExit( EXIT_PROCESS, 2);              /* Error exit */
  143.       }
  144.     node->s_must = n;            /* Set new length */
  145.     memcpy(s_text(node),s,n);        /* Copy new text */
  146.   }
  147.  
  148.  
  149. void            addstring(s,n)
  150. char            *s;        /* String to add */
  151. int            n;        /* Length of string */
  152.   {
  153.     register STRINGNODE    *cur;        /* Current string */
  154.     register STRINGNODE    **pprev;    /* Pointer to previous link */
  155.     STRINGNODE        *new;        /* New string */
  156.     int            i;        /* Index */
  157.     int            j;        /* Count */
  158.     int            k;        /* Count */
  159.  
  160.     if(n <= 0 || n > 127) return;    /* Should never happen */
  161.     i = transtab[*s];            /* Get current index */
  162.     if(i == 0)                /* If no existing list */
  163.       {
  164.     /*
  165.      *  We have to start a new list
  166.      */
  167.     if((i = clists++) >= TRTABLEN/2)
  168.       {                /* If too many string lists */
  169.         fprintf(stderr,"Too many string lists\n");
  170.                     /* Error message */
  171.         DosExit( EXIT_PROCESS, 2);    /* Die */
  172.       }
  173.     stringlist[i] = NULL;        /* Initialize */
  174.     transtab[*s] = i;        /* Set pointer to new list */
  175.     if(!casesen && isalpha(*s)) transtab[*s ^ '\x20'] = i;
  176.                     /* Set pointer for other case */
  177.       }
  178.     else if(stringlist[i] == NULL) return;
  179.                     /* Check for existing 1-byte string */
  180.     if(--n == 0)            /* If 1-byte string */
  181.       {
  182.     freenode(stringlist[i]);    /* Free any existing stuff */
  183.     stringlist[i] = NULL;        /* No record here */
  184.     return;                /* Done */
  185.       }
  186.     ++s;                /* Skip first char */
  187.     pprev = stringlist + i;        /* Get pointer to link */
  188.     cur = *pprev;            /* Get pointer to node */
  189.     while(cur != NULL)            /* Loop to traverse match tree */
  190.       {
  191.     i = (n > cur->s_must)? cur->s_must: n;
  192.                     /* Find minimum of string lengths */
  193.     matchstrings(s,s_text(cur),i,&j,&k);
  194.                     /* Compare the strings */
  195.     if(j == 0)            /* If complete mismatch */
  196.       {
  197.         if(k < 0) break;        /* Break if insertion point found */
  198.         pprev = &(cur->s_alt);    /* Get pointer to alternate link */
  199.         cur = *pprev;        /* Follow the link */
  200.       }
  201.     else if(i == j)            /* Else if strings matched */
  202.       {
  203.         if(i == n)            /* If new is prefix of current */
  204.           {
  205.         reallocnode(cur,s_text(cur),n);
  206.                     /* Shorten text of node */
  207.         if(cur->s_suf != NULL)    /* If there are suffixes */
  208.           {
  209.             freenode(cur->s_suf);
  210.                     /* Suffixes no longer needed */
  211.             cur->s_suf = NULL;
  212.           }
  213.         return;            /* All done */
  214.           }
  215.         pprev = &(cur->s_suf);    /* Get pointer to suffix link */
  216.         if((cur = *pprev) == NULL) return;
  217.                     /* Done if current is prefix of new */
  218.         s += i;            /* Skip matched portion */
  219.         n -= i;
  220.       }
  221.     else                /* Else partial match */
  222.       {
  223.         /*
  224.          *    We must split an existing node.
  225.          *    This is the trickiest case.
  226.          */
  227.         new = newnode(s_text(cur) + j,cur->s_must - j);
  228.                     /* Unmatched part of current string */
  229.         reallocnode(cur,s_text(cur),j);
  230.                     /* Set length to matched portion */
  231.         new->s_suf = cur->s_suf;    /* Current string's suffixes */
  232.         if(k < 0)            /* If new preceded current */
  233.           {
  234.         cur->s_suf = newnode(s + j,n - j);
  235.                     /* FIrst suffix is new string */
  236.         cur->s_suf->s_alt = new;/* Alternate is part of current */
  237.           }
  238.         else            /* Else new followed current */
  239.           {
  240.         new->s_alt = newnode(s + j,n - j);
  241.                     /* Unmatched new string is alternate */
  242.             cur->s_suf = new;    /* New suffix list */
  243.           }
  244.         return;
  245.       }
  246.       }
  247.     *pprev = newnode(s,n);        /* Set pointer to new node */
  248.     (*pprev)->s_alt = cur;        /* Attach alternates */
  249.   }
  250.  
  251.  
  252. int            addfancy(buffer,buflen,seplist)
  253. register char        *buffer;    /* Buffer */
  254. int            buflen;        /* Length of buffer */
  255. char            *seplist;    /* List of separators */
  256.   {
  257.     register char    *bufend;    /* Pointer to end of buffer */
  258.     int            strcnt = 0;    /* String count */
  259.     int            len;        /* String length */
  260.     char        c;        /* One char buffer */
  261.  
  262.     bufend = buffer + buflen;        /* Set end pointer */
  263.     while(buffer < bufend)        /* Loop through all strings */
  264.       {
  265.     len = strncspn(buffer,seplist,bufend - buffer);
  266.                     /* Length of string */
  267.     if(flags & ENDLINE)        /* If match must be at end of line */
  268.       {
  269.         c = buffer[len];        /* Save 1st character past string */
  270.         buffer[len++] = '\r';    /* Carriage return marks end of line */
  271.       }
  272.     if(findlist(buffer,buffer + len) == NULL)
  273.       {                /* If no match within string */
  274.         addstring(buffer,len);    /* Add string to list */
  275.         if(strcnt++ == 0)        /* If first string */
  276.           {
  277.         memcpy(target,buffer,len);
  278.                     /* Save first string in buffer */
  279.         targetlen = len;    /* Remember length */
  280.           }
  281.       }
  282.     buffer += len;            /* Skip over string */
  283.     if(flags & ENDLINE) (--buffer)[0] = c;
  284.                     /* Restore saved character */
  285.     buffer += strnspn(buffer,seplist,bufend - buffer);
  286.                     /* Skip over trailing separators */
  287.       }
  288.     return(strcnt);            /* Return string count */
  289.   }
  290.  
  291.  
  292. int            addplain(buffer,buflen,seplist)
  293. register char        *buffer;    /* String list buffer */
  294. int            buflen;        /* Buffer length */
  295. char            *seplist;    /* List of separators */
  296.   {
  297.     int            strcnt;        /* String count */
  298.     register int    len;        /* String length */
  299.     char        c;        /* One char buffer */
  300.  
  301.     strcnt = 0;
  302.     while((len = strncspn(buffer,seplist,buflen)) > 0)
  303.       {                    /* While not at end of input list */
  304.     if(flags & ENDLINE)        /* If match must be at end of line */
  305.       {
  306.         c = buffer[len];        /* Save 1st character past string */
  307.         buffer[len++] = '\r';    /* Carriage return marks end of line */
  308.       }
  309.     if(strcnt == 0)            /* Save first string */
  310.       {
  311.         strncpy(target,buffer,len);    /* Save string in buffer */
  312.         targetlen = len;        /* Save string length */
  313.       }
  314.     addstring(buffer,len);        /* Add the string to the table */
  315.     if(flags & ENDLINE) buffer[--len] = c;
  316.                     /* Restore saved character */
  317.     buffer += len;            /* Skip the string */
  318.     buflen -= len;
  319.     len = strnspn(buffer,seplist,buflen);
  320.                     /* Skip separators */
  321.     buffer += len;
  322.     buflen -= len;
  323.     ++strcnt;            /* Increment string count */
  324.       }
  325.     return(strcnt);            /* Return string count */
  326.   }
  327.  
  328.  
  329. void            dumplist(node,indent)
  330. register STRINGNODE    *node;        /* Pointer to list to dump */
  331. int            indent;        /* Current length of buffer */
  332.   {
  333.     int            i;        /* Counter */
  334.  
  335.     while(node != NULL)            /* While not at end of list */
  336.       {
  337.     for(i = 0; i < indent; ++i) fputc(' ',stderr);
  338.     fwrite(s_text(node),sizeof(char),node->s_must,stderr);
  339.     fprintf(stderr,"\n");
  340.     if(node->s_suf != NULL)
  341.       dumplist(node->s_suf,indent + node->s_must);
  342.                     /* Recurse to do suffixes */
  343.     node = node->s_alt;        /* Do next alternate in list */
  344.       }
  345.   }
  346.  
  347.  
  348. void            dumpstrings()
  349.   {
  350.     int            i;        /* Index */
  351.  
  352.     for(i = 0; i < TRTABLEN; ++i)    /* Loop through translation table */
  353.       {
  354.     if(transtab[i] == 0) continue;    /* Skip null entries */
  355.     fprintf(stderr,"%c\n",i);    /* Print the first byte */
  356.     dumplist(stringlist[transtab[i]],1);
  357.                     /* Dump the list */
  358.       }
  359.   }
  360.  
  361.  
  362. HFILE            openfile(name)
  363. char            *name;        /* File name */
  364.   {
  365.     HFILE        fd;        /* File descriptor */
  366.  
  367.     if((fd = open(name,0)) == -1)    /* If error opening file */
  368.       {
  369.     fprintf(stderr,"Cannot open %s\r\n",name);
  370.                     /* Print error message */
  371.       }
  372.     return(fd);                /* Return file descriptor */
  373.   }
  374.  
  375.  
  376. void far        thread2()    /* Read thread */
  377.   {
  378.     while(DosSemRequest( &readpending, -1L) == 0)
  379.       {                    /* While there is work to do */
  380.     arrc = DosRead( t2fd, t2buf, t2buflen, &cbread);
  381.                     /* Do the read */
  382.     DosSemClear( &readdone);    /* Signal read completed */
  383.       }
  384.     fprintf(stderr,"Thread 2: DosSemRequest failed\n");
  385.                     /* Print error message */
  386.     DosExit( EXIT_PROCESS, 2);        /* Die */
  387.   }
  388.  
  389.  
  390. void far        thread3()    /* Write thread */
  391.   {
  392.     while(DosSemRequest((long far *) &writepending,-1L) == 0)
  393.       {                    /* While there is work to do */
  394.     awrc = DosWrite(t3fd, t3buf, t3buflen, &cbwrite);
  395.                     /* Do the write */
  396.     DosSemClear( &writedone);    /* Signal write completed */
  397.       }
  398.     fprintf(stderr,"Thread 3: DosSemRequest failed\n");
  399.                     /* Print error message */
  400.     DosExit( EXIT_PROCESS, 2);        /* Die */
  401.   }
  402.  
  403.  
  404. void            startread(fd,buffer,buflen)
  405. HFILE            fd;        /* File handle */
  406. char            *buffer;    /* Buffer */
  407. USHORT            buflen;     /* Buffer length */
  408.   {
  409.     if(pmode)                /* If protected mode */
  410.       {
  411.     if(DosSemRequest( &readdone, -1L) != 0)
  412.       {                /* If we fail to get the semaphore */
  413.         fprintf(stderr,"DosSemRequest failed\n");
  414.                     /* Error message */
  415.         DosExit( EXIT_PROCESS, 2);    /* Die */
  416.       }
  417.     t2fd = fd;            /* Set parameters for read */
  418.     t2buf = buffer;
  419.     t2buflen = buflen;
  420.     DosSemClear( &readpending);    /* Wake thread 2 for read */
  421.     DosSleep(0L);            /* Yield the CPU */
  422.       }
  423.     else arrc = DosRead( fd, buffer, buflen, &cbread);
  424.   }
  425.  
  426.  
  427. int            finishread()
  428.   {
  429.     if(pmode && DosSemWait( &readdone, -1L) != 0)
  430.       {                    /* If protected mode and wait fails */
  431.     fprintf(stderr,"DosSemWait failed\n");
  432.                     /* Print error message */
  433.     DosExit( EXIT_PROCESS, 2);              /* Die */
  434.       }
  435.     return((arrc == 0)? cbread: -1);    /* Return number of bytes read */
  436.   }
  437.  
  438.  
  439. void            startwrite(fd,buffer,buflen)
  440. HFILE            fd;        /* File handle */
  441. char            *buffer;    /* Buffer */
  442. USHORT            buflen;     /* Buffer length */
  443.   {
  444.     if(pmode)                /* If protected mode */
  445.       {
  446.     if(DosSemRequest( &writedone, -1L) != 0)
  447.       {                /* If we fail to get the semaphore */
  448.         fprintf(stderr,"DosSemRequest failed\n");
  449.                     /* Error message */
  450.         DosExit( EXIT_PROCESS, 2);    /* Die */
  451.       }
  452.     t3fd = fd;            /* Set parameters for write */
  453.     t3buf = buffer;
  454.     t3buflen = buflen;
  455.     DosSemClear( &writepending);    /* Wake thread 3 for read */
  456.     DosSleep(0L);            /* Yield the CPU */
  457.       }
  458.     else awrc = DosWrite(fd, buffer, buflen, &cbwrite);
  459.   }
  460.  
  461.  
  462. int            finishwrite()
  463.   {
  464.     if(pmode && DosSemWait( &writedone, -1L) != 0)
  465.       {                    /* If protected mode and wait fails */
  466.     fprintf(stderr,"DosSemWait failed\n");
  467.                     /* Print error message */
  468.     DosExit( EXIT_PROCESS, 2);    /* Die */
  469.       }
  470.     return((awrc == 0)? cbwrite: -1);    /* Return number of bytes written */
  471.   }
  472.  
  473.  
  474. void            write1nobuf(buffer,buflen)
  475. char            *buffer;    /* Buffer */
  476. USHORT            buflen;     /* Buffer length */
  477.   {
  478.     int            cb;        /* Count of bytes written */
  479.  
  480.     if( DosWrite(1, buffer, buflen, &cb) != 0 || cb != buflen)
  481.                     /* If write fails */
  482.       {
  483.     fprintf(stderr,"write error %d\n",awrc);
  484.                     /* Print error message */
  485.     DosExit( EXIT_PROCESS, 2);    /* Die */
  486.       }
  487.   }
  488.  
  489.  
  490. void            write1buf(buffer,buflen)
  491. char            *buffer;    /* Buffer */
  492. USHORT            buflen;     /* Buffer length */
  493.   {
  494.     USHORT        cb;        /* Byte count */
  495.  
  496.     while(buflen > 0)            /* While bytes remain */
  497.       {
  498.     if(awrc != 0)            /* If previous write failed */
  499.       {
  500.         fprintf(stderr,"write error %d\n",awrc);
  501.                     /* Print error message */
  502.         DosExit( EXIT_PROCESS, 2);              /* Die */
  503.       }
  504.     if((cb = ocnt[oi]) == 0)    /* If buffer full */
  505.       {
  506.         startwrite(1,obuf[oi],OUTBUFLEN);
  507.                     /* Write the buffer */
  508.         ocnt[oi] = OUTBUFLEN;    /* Reset count and pointer */
  509.         optr[oi] = obuf[oi];
  510.         oi ^= 1;            /* Switch buffers */
  511.         cb = ocnt[oi];        /* Get space remaining */
  512.       }
  513.     if(cb > buflen) cb = buflen;    /* Get minimum */
  514.     memcpy(optr[oi],buffer,cb);    /* Copy bytes to buffer */
  515.     ocnt[oi] -= cb;            /* Update buffer length and pointers */
  516.     optr[oi] += cb;
  517.     buflen -= cb;
  518.     buffer += cb;
  519.       }
  520.   }
  521.  
  522.  
  523. void            flush1nobuf()
  524.   {
  525.   }
  526.  
  527.  
  528. void            flush1buf()
  529.   {
  530.     int            cb;        /* Byte count */
  531.  
  532.     if((cb = OUTBUFLEN - ocnt[oi]) > 0)    /* If buffer not empty */
  533.       {
  534.     startwrite(1,obuf[oi],cb);    /* Start write */
  535.     if(finishwrite() != cb)        /* If write failed */
  536.       {
  537.         fprintf(stderr,"write error %d\n",awrc);
  538.                     /* Print error message */
  539.         DosExit( EXIT_PROCESS, 2);    /* Die */
  540.       }
  541.       }
  542.   }
  543.  
  544.  
  545. int            grepnull(cp,endbuf,name)
  546. register char        *cp;        /* Buffer pointer */
  547. char            *endbuf;    /* End of buffer */
  548. char            *name;        /* File name */
  549.   {
  550.     return(0);                /* Do nothing */
  551.   }
  552.  
  553.  
  554. int            grepbuffer(startbuf,endbuf,name)
  555. char            *startbuf;    /* Start of buffer */
  556. char            *endbuf;    /* End of buffer */
  557. char            *name;        /* File name */
  558.   {
  559.     register char    *cp;        /* Buffer pointer */
  560.     char        *lastmatch;    /* Last matching line */
  561.     int            linelen;    /* Line length */
  562.     int            namlen = 0;    /* Length of name */
  563.     char        lnobuf[LNOLEN];    /* Line number buffer */
  564.     char        nambuf[LINELEN];/* Name buffer */
  565.  
  566.     cp = startbuf;            /* Initialize to start of buffer */
  567.     lastmatch = cp;            /* No previous match yet */
  568.     while((cp = (*find)(cp,endbuf)) != NULL)
  569.       {                    /* While matches are found */
  570.     if((flags & BEGLINE) && cp[-1] != '\n' && cp > startbuf)
  571.       {                /* If begin line conditions not met */
  572.         ++cp;            /* Skip first char of match */
  573.         continue;            /* Keep looking */
  574.       }
  575.     status = 0;            /* Match found */
  576.     if(flags & NAMEONLY)        /* If filename only wanted */
  577.       {
  578.         (*write1)(nambuf,sprintf(nambuf,"%s\r\n",name));
  579.                     /* Print the name */
  580.         return(1);            /* Punt remainder of buffer */
  581.       }
  582.     cp -= revfind(cp,'\n',cp - startbuf);
  583.                     /* Point at last linefeed */
  584.     if(*cp == '\n') ++cp;        /* Point at start of line */
  585.     if(flags & SHOWNAME)        /* If name wanted */
  586.       {
  587.         if(namlen == 0) namlen = sprintf(nambuf,"%s:",name);
  588.                     /* Format name if not done already */
  589.         (*write1)(nambuf,namlen);    /* Show name */
  590.       }
  591.     if(flags & LINENOS)        /* If line number wanted */
  592.       {
  593.         lineno += countlines(lastmatch,cp);
  594.                     /* Count lines since last match */
  595.         (*write1)(lnobuf,sprintf(lnobuf,"%d:",lineno));
  596.                     /* Print line number */
  597.         lastmatch = cp;        /* New last match */
  598.       }
  599.     linelen = strncspn(cp,"\n",endbuf - cp) + 1;
  600.                     /* Calculate line length */
  601.     (*write1)(cp,linelen);        /* Print the line */
  602.     cp += linelen;            /* Skip the line */
  603.       }
  604.     if(flags & LINENOS) lineno += countlines(lastmatch,endbuf);
  605.                     /* Count remaining lines in buffer */
  606.     return(0);                /* Keep searching */
  607.   }
  608.  
  609.  
  610. void            showv(name,lastmatch,thismatch)
  611. char            *name;
  612. register char        *lastmatch;
  613. char            *thismatch;
  614.   {
  615.     register int    linelen;
  616.     int            namlen = 0;    /* Length of name */
  617.     char        lnobuf[LNOLEN];    /* Line number buffer */
  618.     char        nambuf[LINELEN];/* Name buffer */
  619.  
  620.     if(flags & (SHOWNAME | LINENOS))
  621.       {
  622.     while(lastmatch < thismatch)
  623.       {
  624.         if(flags & SHOWNAME)    /* If name wanted */
  625.           {
  626.         if(namlen == 0) namlen = sprintf(nambuf,"%s:",name);
  627.                     /* Format name if not done already */
  628.         (*write1)(nambuf,namlen);
  629.                     /* Write the name */
  630.           }
  631.         if(flags & LINENOS)
  632.           {
  633.         (*write1)(lnobuf,sprintf(lnobuf,"%d:",lineno++));
  634.           }
  635.         linelen = strncspn(lastmatch,"\n",thismatch - lastmatch) + 1;
  636.         (*write1)(lastmatch,linelen);
  637.         lastmatch += linelen;
  638.       }
  639.       }
  640.     else (*write1)(lastmatch,thismatch - lastmatch);
  641.   }
  642.  
  643.  
  644. int            grepvbuffer(startbuf,endbuf,name)
  645. char            *startbuf;    /* Start of buffer */
  646. char            *endbuf;    /* End of buffer */
  647. char            *name;        /* File name */
  648.   {
  649.     register char    *cp;        /* Buffer pointer */
  650.     register char    *lastmatch;    /* Pointer to line after last match */
  651.  
  652.     cp = startbuf;            /* Initialize to start of buffer */
  653.     lastmatch = cp;
  654.     while((cp = (*find)(cp,endbuf)) != NULL)
  655.       {
  656.     if((flags & BEGLINE) && cp[-1] != '\n' && cp > startbuf)
  657.       {                /* If begin line conditions not met */
  658.         ++cp;            /* Skip first char of match */
  659.         continue;            /* Keep looking */
  660.       }
  661.     status = 1;            /* Match found */
  662.     if(flags & NAMEONLY) return(1);    /* Skip rest of file if NAMEONLY */
  663.     cp -= revfind(cp,'\n',cp - startbuf);
  664.                     /* Point at last linefeed */
  665.     if(*cp == '\n') ++cp;        /* Point at start of line */
  666.     showv(name,lastmatch,cp);    /* Show from last match to this */
  667.     cp += strncspn(cp,"\n",endbuf - cp) + 1;
  668.                     /* Skip over line with match */
  669.     lastmatch = cp;            /* New "last" match */
  670.     ++lineno;            /* Increment line count */
  671.       }
  672.     if(!(flags & NAMEONLY)) showv(name,lastmatch,endbuf);
  673.                     /* Show buffer tail if not NAMEONLY */
  674.     return(0);                /* Keep searching file */
  675.   }
  676.  
  677.  
  678. void            qgrep(name,fd)
  679. char            *name;        /* File name */
  680. HFILE            fd;        /* File descriptor */
  681.   {
  682.     register int    cb;        /* Byte count */
  683.     register char    *cp;        /* Buffer pointer */
  684.     char        *endbuf;    /* End of buffer */
  685.     int            taillen;    /* Length of buffer tail */
  686.     int            bufi;        /* Buffer index */
  687.     char        line[LINELEN];    /* Line buffer */
  688.  
  689.     lineno = 1;                /* File starts on line 1 */
  690.     taillen = 0;            /* No buffer tail yet */
  691.     bufi = 0;                /* Initialize buffer index */
  692.     cp = bufptr[0];            /* Initialize to start of buffer */
  693.     finishread();            /* Make sure no I/O activity */
  694.     arrc = DosRead( fd, cp, FILBUFLEN, &cbread);
  695.                     /* Do first read synchronously */
  696.     while((cb = finishread()) + taillen > 0)
  697.       {                    /* While search incomplete */
  698.     if(cb == 0)            /* If buffer tail is all that's left */
  699.       {
  700.         taillen = 0;        /* Set tail length to zero */
  701.         *cp++ = '\r';        /* Add end of line sequence */
  702.         *cp++ = '\n';
  703.         endbuf = cp;        /* Note end of buffer */
  704.       }
  705.     else                /* Else start next read */
  706.       {
  707.         taillen = revfind(cp + cb - 1,'\n',cb);
  708.                     /* Find length of partial line */
  709.         endbuf = cp + cb - taillen;    /* Get pointer to end of buffer */
  710.         cp = bufptr[bufi ^ 1];    /* Pointer to other buffer */
  711.         memcpy(cp,endbuf,taillen);    /* Copy tail to head of other buffer */
  712.         cp += taillen;        /* Skip over tail */
  713.         startread(fd,cp,(FILBUFLEN - taillen) & (~0 << LG2SECLEN));
  714.                     /* Start next read */
  715.       }
  716.     if((*grep)(bufptr[bufi],endbuf,name)) return;
  717.                     /* Done if NAMEONLY and match found */
  718.     bufi ^= 1;            /* Switch buffers */
  719.       }
  720.     if((flags & (NAMEONLY | INVERT)) == (NAMEONLY | INVERT))
  721.       (*write1)(line,sprintf(line,"%s\r\n",name));
  722.                     /* Write name if -lv */
  723.   }
  724.  
  725.  
  726. void            usage(verbose)
  727. int            verbose;    /* Verbose message flag */
  728.   {
  729.     static char        *opts[] =
  730.       {
  731.     "-? - print this message",
  732.     "-B - match pattern if at beginning of line",
  733.     "-E - match pattern if at end of line",
  734.     "-l - print only file name if file contains match",
  735.     "-n - print line number before each matching line",
  736.     "-v - print only lines not containing a match",
  737.     "-x - print lines that match exactly (-BE)",
  738.     "-y - treat upper and lower case as equivalent",
  739.     "-e - treat next argument as the search string",
  740.     "-f - read search strings from file named by next argument",
  741.     "-i - read file list from file named by next argument",
  742.     0
  743.       };
  744.     register char    **opt = opts;    /* Option list */
  745.  
  746.     fprintf(stderr,"usage: CPGREP [-?BElnvxy][-e][-f <file>][-i <file>][<strings>][<files>]\n");
  747.     if(verbose)                /* If verbose message wanted */
  748.       {
  749.     while(*opt != 0) fprintf(stderr,"%s\n",*opt++);
  750.                     /* Print option list */
  751.       }
  752.     DosExit( EXIT_PROCESS, 2);        /* Error exit */
  753.   }
  754.  
  755.  
  756. void            main(argc,argv)
  757. int            argc;
  758. char            **argv;
  759.   {
  760.     register char    *cp;
  761.     HFILE        fd;
  762.     FILE        *fi;
  763.     char        filnam[FILNAMLEN];
  764.     USHORT        handType;
  765.     USHORT        handAttrib;
  766.     int            i;
  767.     char        *inpfile = NULL;
  768.     int         j;
  769.     char        *seplist = " \t";
  770.     int            strcnt;
  771.     char        *strfile = NULL;
  772.     long        start;        /* Start time */
  773.     int            (*add)();
  774.     BYTE        t2stk[2*STKLEN];  /* Read thread stack */
  775.     BYTE        t3stk[2*STKLEN];  /* Write thread stack */
  776.     long        time();        /* Time and date in seconds */
  777.  
  778.     DosGetMachineMode((char far *) &pmode);
  779.     flags = 0;
  780.     for(i = 1; i < argc && argv[i][0] == '-'; ++i)
  781.       {
  782.     switch(argv[i][1])
  783.       {
  784.         case 'f':
  785.         case 'i':
  786.           if(i == argc - 1)
  787.         {
  788.           fprintf(stderr,"File name missing after -%c\n",argv[i][1]);
  789.           DosExit( EXIT_PROCESS, 2);
  790.         }
  791.           if(argv[i++][1] == 'i') inpfile = argv[i];
  792.           else strfile = argv[i];
  793.           break;
  794.  
  795.         case '?':
  796.         case 'B':
  797.         case 'E':
  798.         case 'N':
  799.         case 'S':
  800.         case 'd':
  801.         case 'l':
  802.         case 'n':
  803.         case 't':
  804.         case 'v':
  805.         case 'x':
  806.         case 'y':
  807.           for(cp = &argv[i][1]; *cp != '\0'; ++cp)
  808.         {
  809.           switch(*cp)
  810.             {
  811.               case '?':
  812.             usage(1);    /* Verbose usage message */
  813.  
  814.               case 'B':
  815.             flags |= BEGLINE;
  816.             break;
  817.  
  818.               case 'E':
  819.             flags |= ENDLINE;
  820.             break;
  821.  
  822.               case 'N':
  823.             grep = grepnull;
  824.             break;
  825.  
  826.               case 'S':
  827.             pmode = 0;    /* Force synchronous I/O */
  828.             break;
  829.  
  830.               case 'd':
  831.             flags |= DEBUG;
  832.             break;
  833.  
  834.               case 'l':
  835.             flags |= NAMEONLY;
  836.             break;
  837.  
  838.               case 'n':
  839.             flags |= LINENOS;
  840.             break;
  841.  
  842.               case 't':
  843.             flags |= TIMER;
  844.             break;
  845.  
  846.               case 'v':
  847.             status = 0;    /* Assume success */
  848.             flags |= INVERT;
  849.             grep = grepvbuffer;
  850.             break;
  851.  
  852.               case 'x':
  853.             flags |= BEGLINE | ENDLINE;
  854.             break;
  855.  
  856.               case 'y':
  857.             casesen = 0;
  858.             break;
  859.  
  860.               default:
  861.             fprintf(stderr,"-%c ignored\n",*cp);
  862.             break;
  863.             }
  864.             }
  865.           break;
  866.  
  867.         case 'e':
  868.           if(strfile == NULL)
  869.         {
  870.           ++i;
  871.           seplist = "";        /* Allow anything in string */
  872.           goto endfor0;
  873.         }
  874.           /* Drop through to "default" */
  875.  
  876.         default:
  877.           fprintf(stderr,"%s ignored\n",argv[i]);
  878.           break;
  879.       }
  880.       }
  881.     endfor0:
  882.  
  883.     if(i == argc && strfile == NULL) usage(0);
  884.                     /* Simple usage message if arg error */
  885.     if(flags & TIMER) start = time(NULL);
  886.                     /* Get start time if timer on */
  887.     if(pmode)                /* Initialize semaphores and threads */
  888.       {
  889.     TID threadId;
  890.  
  891.     DosSemClear( &readdone);
  892.     DosSemClear( &writedone);
  893.     DosSemSet( &readpending);
  894.     DosSemSet( &writepending);
  895.     if(DosCreateThread(thread2, &threadId, t2stk + 2*STKLEN) != 0 ||
  896.        DosCreateThread(thread3, &threadId, t3stk + 2*STKLEN) != 0)
  897.       {                /* If thread creation fails */
  898.         fprintf(stderr,"Failed to create child threads\n");
  899.                     /* Print error message */
  900.         DosExit( EXIT_PROCESS, 2);    /* Die */
  901.       }
  902.       }
  903.     setmode(fileno(stdout),O_BINARY);
  904.     add = addplain;            /* Assume plain string adds */
  905.     if(strfile != NULL)            /* If strings from file */
  906.       {
  907.     if(!(flags & BEGLINE)) add = addfancy;
  908.                     /* Try to add intelligently */
  909.     if((fd = open(strfile,0)) == -1)
  910.       {                /* If open fails */
  911.         fprintf(stderr,"Cannot read strings from %s\n",strfile);
  912.         DosExit( EXIT_PROCESS, 2);              /* Print message and die */
  913.       }
  914.     for(cp = filbuf, j = 0; (j = read(fd,cp,FILBUFLEN*2 - j)) > 0; cp += j);
  915.                     /* Read strings file into buffer */
  916.     j = cp - filbuf;        /* Get total length of buffer */
  917.     close(fd);            /* Close strings file */
  918.     filbuf[j] = '\0';        /* Null-terminate the buffer */
  919.     cp = filbuf;            /* Set pointer to string list */
  920.     seplist = "\r\n";        /* Only '\r' and '\n' are separators */
  921.       }
  922.     else                /* Else strings on command line */
  923.       {
  924.     cp = argv[i++];            /* Set pointer to strings */
  925.     j = strlen(cp);            /* Get length of strings */
  926.       }
  927.     if((strcnt = (*add)(cp,j,seplist)) == 0)
  928.       {                    /* If no strings */
  929.     fprintf(stderr,"No search strings\n");
  930.     DosExit( EXIT_PROCESS, 2);    /* Print error message and die */
  931.       }
  932.  
  933.     /*
  934.      *  Check type of handle for std. out.
  935.      */
  936.     if(DosQHandType(fileno(stdout), &handType, &handAttrib) != 0)
  937.       {                    /* If error */
  938.     fprintf(stderr,"Standard output bad handle\n");
  939.                     /* Print error message */
  940.     DosExit( EXIT_PROCESS, 2);    /* Die */
  941.       }
  942.     if(handType != 0 && (handAttrib & ISCOT))
  943.       {                 /* If handle is console output */
  944.     write1 = write1nobuf;        /* Use unbuffered output */
  945.     flush1 = flush1nobuf;
  946.       }
  947.  
  948.     if(strcnt > 1)            /* If more than one string */
  949.       {
  950.     if(flags & DEBUG)        /* Print debug info maybe */
  951.       {
  952.         fprintf(stderr,"Here are the strings:\n");
  953.         dumpstrings();
  954.       }
  955.       }
  956.     else if(casesen) find = findone;    /* Else use findone() */
  957.     if(inpfile != NULL)            /* If file list from file */
  958.       {
  959.     flags |= SHOWNAME;        /* Always show name of file */
  960.     if((fi = fopen(inpfile,"r")) == NULL)
  961.       {                /* If open fails */
  962.         fprintf(stderr,"Cannot read file list from %s\r\n",inpfile);
  963.                     /* Error message */
  964.         DosExit( EXIT_PROCESS, 2);    /* Error exit */
  965.       }
  966.     while(fgets(filnam,FILNAMLEN,fi) != NULL)
  967.       {                /* While there are names */
  968.         filnam[strcspn(filnam,"\r\n")] = '\0';
  969.                     /* Null-terminate the name */
  970.         if((fd = openfile(filnam)) == -1) continue;
  971.                     /* Skip file if it cannot be opened */
  972.         qgrep(filnam,fd);        /* Do the work */
  973.         close(fd);            /* Close the file */
  974.       }
  975.     fclose(fi);            /* Close the list file */
  976.       }
  977.     else if(i == argc)
  978.       {
  979.     flags &= ~(NAMEONLY | SHOWNAME);
  980.     setmode(fileno(stdin),O_BINARY);
  981.     qgrep(NULL,fileno(stdin));
  982.       }
  983.     if(argc > i + 1) flags |= SHOWNAME;
  984.     for(; i < argc; ++i)
  985.       {
  986.     if((fd = openfile(argv[i])) == -1) continue;
  987.     qgrep(argv[i],fd);
  988.     close(fd);
  989.       }
  990.     (*flush1)();
  991.     if(flags & TIMER) fprintf(stderr,"%ld seconds\n",time(NULL) - start);
  992.                     /* Print elapsed time if timer on */
  993.     DosExit( EXIT_PROCESS, status);
  994.   }
  995.