/************************************************************************/
/*                                                                      */
/*      GGrep -- Regular expression search using Grouse architecture    */
/*                                                                      */
/*      behoffski is seeing if his way of counting words using an       */
/*      optimised switch statement in assembly can be extended          */
/*      somewhat dramatically to include regular expressions.  If       */
/*      so, the performance should be rather astonishing.  This         */
/*      is the first step: to get a similar, slower table-driven        */
/*      version running in C.  This is to teach behoffski the full      */
/*      joys of regular expression searching.                           */
/*                                                                      */
/*      Started at 10pm on Saturday, 13-May-95.                         */
/*                                                                      */
/*      If you want to find the program's entry point (main),           */
/*      look in Platform.  The code is cut this way so that             */
/*      this module remains portable.                                   */
/*                                                                      */
/*      Copyright (C) 1995-2000 Grouse Software.  All rights reserved.  */
/*      Written for Grouse by behoffski (Brenton Hoff).                 */
/*                                                                      */
/*      Free software: no warranty; use anywhere is ok; spread the      */
/*      sources; note any mods; share variations and derivatives        */
/*      (including sending to behoffski@grouse.com.au).                 */
/*                                                                      */
/************************************************************************/

#include "ascii.h"
#include <compdef.h>
#include "main.h"
#include "platform.h"
#include "regexp00.h"
#include "scanfile.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tracery.h"

/*Maximum RE text that can be read from file*/
#define RE_TEXT_SIZE_MAX                10240

/*Parameter for module diagnosis strings (?? should be in compdef?)*/
#define DIAGNOSIS_LENGTH_MAX            128

/*Codes to select RE syntax and search behaviour*/
typedef enum {
        SEARCH_STYLE_UNSPECIFIED,
        SEARCH_AS_FGREP,
        SEARCH_AS_GREP,
        SEARCH_AS_EGREP
} SEARCH_STYLE;

/*Program options*/
typedef struct {
        SEARCH_STYLE SearchStyle;

} OPT_STRUCT;

/*Permanent variables referenced within this module*/
typedef struct {
        /*Options specified by client*/
        OPT_STRUCT Opt;

        /*Flags modifying regular expression interpretation*/
        LWORD RegexpConfig;

        /*Search and display options used to configure ScanFile*/
        LWORD ScanOptions;
        LWORD ReportingOptions;

        /*Program return code*/
        UINT ReturnCode;

} GREP_MODULE_CONTEXT;

module_scope GREP_MODULE_CONTEXT gGgrep;


/************************************************************************/
/*                                                                      */
/*      Init -- Prepare module for operation                            */
/*                                                                      */
/************************************************************************/
public_scope void
GGrep_Init(void)
{
        /*Program defaults to successful run*/
        gGgrep.ReturnCode = MAIN_RETURN_MATCHED;

} /*Init*/


/************************************************************************/
/*                                                                      */
/*      ReadREFromFile -- Use first line of file as RE                  */
/*                                                                      */
/*      This function is provided to support the -f option of           */
/*      GGrep.  It attempts to open the file specified, then            */
/*      read the first line as RE text.  Returns a pointer to the       */
/*      start of the buffer if successful (with NUL terminator),        */
/*      or else NULL if not successful.                                 */
/*                                                                      */
/************************************************************************/
module_scope CHAR *
GGrep_ReadREFromFile(CHAR *pFilename)
{
        FILE *f;
        CHAR *pREBuf;
        UINT len;

        /*Open named file for read*/
        f = fopen(pFilename, "r");
        if (f == NULL) {
                /*Sorry, unable to open file*/
                fprintf(stderr, "ggrep: Unable to open file %s", pFilename);
                return NULL;

        }

        /*Acquire buffer to store RE text*/
        pREBuf = malloc(RE_TEXT_SIZE_MAX);
        if (pREBuf == NULL) {
                /*Sorry, unable to acquire memory for line buffer*/
                fprintf(stderr, "ggrep: No memory for line buffer");
                return NULL;

        }

        /*Read line into buffer*/
        (void) fgets(pREBuf, RE_TEXT_SIZE_MAX, f);

        /*Does the line end with a LF?*/
        len = strlen(pREBuf);
        if (pREBuf[len - 1] != LF) {
                /*No, line is too big for buffer*/
                fprintf(stderr, "ggrep: line too long in file %s", pFilename);
                free(pREBuf);
                return NULL;

        } else {
                /*Yes, remove it*/
                pREBuf[len - 1] = NUL;
        }

        /*Finished with specifier file*/
        fclose(f);

        /*Report RE to caller*/
        return pREBuf;

} /*ReadREFromFile*/


/************************************************************************/
/*                                                                      */
/*      ReturnCode -- Allow other modules to write program return       */
/*                                                                      */
/*      Other modules may find faults during operation but be able      */
/*      keep going.  These modules must be able to report the faults    */
/*      to the main program so that the program return code can         */
/*      be updated correctly.                                           */
/*                                                                      */
/*      The use of module Main here is very dubious... sigh.            */
/*      This function might be more appropriate as part of Platform.    */
/*                                                                      */
/************************************************************************/
public_scope void
Main_ReturnCode(UINT Code)
{
        /*Is this code more severe than the current return code?*/
        if (Code > gGgrep.ReturnCode) {
                /*Yes, remember this code as program result*/
                gGgrep.ReturnCode = Code;

        }

} /*ReturnCode*/


/************************************************************************/
/*                                                                      */
/*      Usage -- Tell the user what we expect                           */
/*                                                                      */
/************************************************************************/
module_scope void
GGrep_Usage(void)
{
        /*Display usage text*/
        fprintf(stderr,
                "Usage: ggrep [-bcFGhHilLnrRvVwx]"
                " [-CDMcOx -Tx]"
                " ([-e] regexp | -f file) [files]\n");
        /*Sadly, the debug/optimisation/trace usage text is very cryptic*/

        exit(MAIN_RETURN_FAULT);

} /*Usage*/


/************************************************************************/
/*                                                                      */
/*      Version -- Report program version and other info                */
/*                                                                      */
/************************************************************************/
module_scope void
GGrep_Version(void)
{
        /*Display program name and version*/
        fprintf(stderr, "ggrep (Grouse Grep) v2.00  14-Feb-2000\n"
                "Copyright (c) Grouse Software 1995-2000.  " 
                "All rights reserved.\n"
                "Written for Grouse by behoffski (Brenton Hoff).\n");

        exit(0);

} /*Version*/


/************************************************************************/
/*                                                                      */
/*      OptiOpts -- Parse options to enable/disable optimisations       */
/*                                                                      */
/*      Yet another function inspired by the ludicrous lengths          */
/*      to which behoffski is going to build a (hopefully)              */
/*      worthwhile test rig.  The match reported by -M isn't            */
/*      strictly correct in some cases as optimisations can trim        */
/*      the ends away.  Being able to turn off optimisations at         */
/*      will allows this case to report correctly, as well as           */
/*      providing a way of checking that each layer of pattern          */
/*      interpretation is correct, not merely the final,                */
/*      optimised result.                                               */
/*                                                                      */
/************************************************************************/
module_scope void
GGrep_OptiOpts(CHAR *pOpt)
{
        for (;;) {
                if (*pOpt == NUL) {
                        break;
                }
                switch (*pOpt++) {
                case 'A':
                        gGgrep.ScanOptions |=  SCANFILE_OPT_APPROXIMATE;
                        break;
                case 'a':
                        gGgrep.ScanOptions &= ~SCANFILE_OPT_APPROXIMATE;
                        break;
                case 'B':
                        gGgrep.ScanOptions |=  SCANFILE_OPT_TUNED_BM;
                        break;
                case 'b':
                        gGgrep.ScanOptions &= ~SCANFILE_OPT_TUNED_BM;
                        break;
                case 'E':
                        gGgrep.ScanOptions |=  SCANFILE_OPT_EASIEST_FIRST;
                        break;
                case 'e':
                        gGgrep.ScanOptions &= ~SCANFILE_OPT_EASIEST_FIRST;
                        break;
                case 'S':
                        gGgrep.ScanOptions |=  SCANFILE_OPT_SKIP;
                        break;
                case 's':
                        gGgrep.ScanOptions &= ~SCANFILE_OPT_SKIP;
                        break;
                case 'T':
                        gGgrep.ScanOptions |=  SCANFILE_OPT_SELF_TUNED_BM;
                        break;
                case 't':
                        gGgrep.ScanOptions &= ~SCANFILE_OPT_SELF_TUNED_BM;
                        break;

                /*default: ignored*/
                }
        }

} /*OptiOpts*/


/************************************************************************/
/*                                                                      */
/*      ConvertLongOption -- Translate "--LongName" options             */
/*                                                                      */
/*      Converts most -- if not all -- long-style (GNU) option          */
/*      names to their shorter equivalents.  The function               */
/*      rewrites the in/out pointer to point to the rewritten           */
/*      option code(s).  If a long name has no short equivalent,        */
/*      the option should be implemented here, and pOpt changed         */
/*      to NULL to signal the end of the processing.                    */
/*                                                                      */
/*      Returns TRUE if the option was recognised and handled           */
/*      correctly, and FALSE otherwise (usually because the             */
/*      option name wasn't known).                                      */
/*                                                                      */
/************************************************************************/
module_scope BOOL
GGrep_ConvertLongOption(CHAR **ppOpt)
{
        CHAR *pOpt = *ppOpt;

        switch (strlen(pOpt)) {
        case 4:
                if (strcmp(pOpt,        "help") == 0) {
                        pOpt = NULL;
                        GGrep_Usage();
                        break;

                }
                return FALSE;
                /*break;*/

        case 5:
                if (strcmp(pOpt,        "quiet") == 0) {
                        pOpt = "q";
                        break;
                } else if (strcmp(pOpt, "count") == 0) {
                        pOpt = "c";
                        break;
                }
                return FALSE;

        case 6:
                if (strcmp(pOpt,        "silent") == 0) {
                        pOpt = "?";
                        break;
                }
                return FALSE;

        case 7:
                if (strcmp(pOpt,        "version") == 0) {
                        pOpt = "V";
                        break;
                }
                return FALSE;

        case 9:
                if (strcmp(pOpt,        "recursive") == 0) {
                        pOpt = "r";
                        break;
                }
                return FALSE;

        case 11:
                if (strcmp(pOpt,        "no-filename") == 0) {
                        pOpt = "h";
                        break;
                } else if (strcmp(pOpt, "ignore-case") == 0) {
                        pOpt = "i";
                        break;
                } else if (strcmp(pOpt, "byte-offset") == 0) {
                        pOpt = "b";
                        break;
                } else if (strcmp(pOpt, "line-number") == 0) {
                        pOpt = "n";
                        break;
                } else if (strcmp(pOpt, "word-regexp") == 0) {
                        pOpt = "w";
                        break;
                } else if (strcmp(pOpt, "line-regexp") == 0) {
                        pOpt = "x";
                        break;
                }
                return FALSE;

        case 12:
                if (strcmp(pOpt,        "basic-regexp") == 0) {
                        pOpt = "G";
                        break;
                } else if (strcmp(pOpt, "revert-match") == 0) {
                        pOpt = "v";
                        break;
                }
                return FALSE;

        case 13:
                if (strcmp(pOpt,        "fixed-strings") == 0) {
                        pOpt = "F";
                        break;
                }
                return FALSE;

        case 18:
                if (strcmp(pOpt,        "files-with-matches") == 0) {
                        pOpt = "l";
                        break;
                }
                return FALSE;

        case 19:
                if (strcmp(pOpt,        "files-without-match") == 0) {
                        pOpt = "L";
                        break;
                }
                return FALSE;

        default:
                /*Unrecognised option*/
                return FALSE;

        }

        /*Option handled, write result to caller and report success*/
        *ppOpt = pOpt;
        return TRUE;


} /*ConvertLongOption*/


/************************************************************************/
/*                                                                      */
/*      main -- Co-ordinate execution of program                        */
/*                                                                      */
/************************************************************************/
public_scope INT
GGrep_main(UINT argc, CHAR **argv)
{
        RegExp_Specification *pCompactRE = NIL;
        CHAR *pRegexpText = NULL;
        CHAR *pArg;
        CHAR *pOpt;
        CHAR *pREFilename;
        CHAR Diagnosis[DIAGNOSIS_LENGTH_MAX];
        BOOL SuppressFilename = FALSE;
        CHAR *pProgName;

        /*If no arguments, give Usage message to guide user*/
        if (argc == 1) {
                GGrep_Usage();
        }
        argv++;

        /*Prepare default options*/
        gGgrep.Opt.SearchStyle = SEARCH_STYLE_UNSPECIFIED;
        gGgrep.ReportingOptions = MATCHENG_RPT_LINE;
        gGgrep.ScanOptions = (0 
                              | SCANFILE_OPT_APPROXIMATE
                              | SCANFILE_OPT_EASIEST_FIRST
                              | SCANFILE_OPT_SKIP 
                              | SCANFILE_OPT_SELF_TUNED_BM
                              );
        gGgrep.RegexpConfig = REGEXP_CONFIG_CLASS_CONV_CASE;

        /*Select search style if it's indicated by the program name*/
        pProgName = Platform_ProgramName();
        if (strcmp(pProgName, "fgrep") == 0) {
                gGgrep.Opt.SearchStyle = SEARCH_AS_FGREP;
        } else if (strcmp(pProgName, "egrep") == 0) {
                gGgrep.Opt.SearchStyle = SEARCH_AS_EGREP;
        }

        /*Loop through arguments, processing options etc*/
        for (;; argv++) {
                /*Have we run out of arguments?*/
                if (--argc == 0) {
                        /*Yes, was a RE specified by an option?*/
                        if (pRegexpText == NULL) {
                                /*No, a search without RE is meaningless*/
                                GGrep_Usage();
                        }

                        /*User wants to use stdin as input*/
                        break;

                }

                /*Refer to next argument via convenient pointer*/
                pArg = *argv;

                /*Is the next argument an option specifier?*/
                if (pArg[0] != '-') {
                        /*No, user has finished giving options*/
                        break;
                }

                /*Yes, decode option and record selection*/
                pOpt = &pArg[1];

                /*Did we find a "-" by itself?*/
                if (*pOpt == NUL) {
                        /*Yes, this is the RE to search for*/
                        break;
                }

                /*Do we have a "--longname"-style option?*/
                if ((pOpt[0] == '-') && (pOpt[1] != NUL)) {
                        /*Yes, convert long name to short name*/
                        pOpt++;
                        if (! GGrep_ConvertLongOption(&pOpt)) {
                                /*Sorry, option not recognised*/
                                fprintf(stderr, "ggrep: Unknown option: %s\n",
                                        pOpt);
                                GGrep_Usage();
                                break;
                        }

                        /*Did the conversion leave us with an option code?*/
                        if (pOpt == NULL) {
                                /*No, move to next option (if any)*/
                                continue;
                        }

                }

DecodeOption:
                /*Act on next option byte*/
                switch (*pOpt++) {
                case 'b':
                        /*Include byte offset in display*/
                        gGgrep.ReportingOptions |=
                                        MATCHENG_RPT_BYTEOFFSET;
                        break;

                case 'c':
                        /*Count selected lines and don't display them*/
                        gGgrep.ReportingOptions |= MATCHENG_RPT_LINECOUNT;
                        gGgrep.ReportingOptions &= ~MATCHENG_RPT_LINE;
                        break;

                case 'C':
                        /*Debug: Display compiled RE before expansion*/
                        gGgrep.ScanOptions |= SCANFILE_DEBUG_COMPILED;
                        break;

                case 'D':
                        /*Display state tables (should move over to Tracery)*/
                        gGgrep.ScanOptions |= SCANFILE_DEBUG_DISPLAY;
                        break;

                case 'e':
                        /*Have we exhausted the current option word?*/
                        if (*pOpt != NUL) {
                                /*No, use remainder of word as RE*/
                                pRegexpText = pOpt;

                        } else {
                                /*Yes, next arg is RE*/
                                if (--argc == 0) {
                                        GGrep_Usage();
                                }
                                pRegexpText = *++argv;
                        }

                        /*Finished handling current option word(s)*/
                        continue;
                        break;

                case 'E':
                        /*Select extended RE syntax*/
                        switch (gGgrep.Opt.SearchStyle) {
                        case SEARCH_STYLE_UNSPECIFIED:
                        case SEARCH_AS_EGREP:
                                gGgrep.Opt.SearchStyle = SEARCH_AS_EGREP;
                                break;

                        default:
                                fprintf(stderr, 
                             "you may specify only one of -E, -F or -G\n");
                                exit(MAIN_RETURN_FAULT);
                                break;
                        }
                        break;

                case 'f':
                        /*Have we exhausted the current option word?*/
                        if (*pOpt != NUL) {
                                /*No, use remainder of word as file name*/
                                pREFilename = pOpt;

                        } else {
                                /*Yes, next word contains RE file name*/
                                if (--argc == 0) {
                                        GGrep_Usage();
                                }
                                pREFilename = *++argv;
                        }
                        pRegexpText = GGrep_ReadREFromFile(pREFilename);
                        if (pRegexpText == NULL) {
                                /*Failed to get RE from file*/
                                return MAIN_RETURN_FAULT;
                        }

                        /*Finished handling current option word(s)*/
                        continue;
                        break;

                case 'F':
                        /*Select fixed-string RE syntax and operation*/
                        switch (gGgrep.Opt.SearchStyle) {
                        case SEARCH_STYLE_UNSPECIFIED:
                        case SEARCH_AS_FGREP:
                                gGgrep.Opt.SearchStyle = SEARCH_AS_FGREP;
                                break;

                        default:
                                fprintf(stderr, 
                             "you may specify only one of -E, -F or -G\n");
                                exit(MAIN_RETURN_FAULT);
                                break;
                        }
                        break;

                case 'G':
                        /*Select basic RE syntax*/
                        /*??*/
                        switch (gGgrep.Opt.SearchStyle) {
                        case SEARCH_STYLE_UNSPECIFIED:
                        case SEARCH_AS_GREP:
                                gGgrep.Opt.SearchStyle = SEARCH_AS_GREP;
                                break;

                        default:
                                fprintf(stderr, 
                             "you may specify only one of -E, -F or -G\n");
                                exit(MAIN_RETURN_FAULT);
                                break;
                        }
                        break;

                case 'h':
                        /*Suppress file name prefix if displaying lines*/
                        SuppressFilename = TRUE;
                        break;

                case 'H':
                        /*Highlight match as part of display*/
                        gGgrep.ReportingOptions |= MATCHENG_RPT_HIGHLIGHT;
                        break;

                case 'i':
                        /*RE becomes case-insensitive*/
                        gGgrep.RegexpConfig |= REGEXP_CONFIG_IGNORE_CASE;
                        break;

                case 'l':
                        /*Only show filename for matching files*/
                        gGgrep.ReportingOptions &= ~MATCHENG_RPT_LINE;
                        gGgrep.ReportingOptions |= MATCHENG_RPT_FILENAME | 
                                MATCHENG_RPT_NORMAL_MATCH_SENSE;
                        break;

                case 'L':
                        /*Only show filename for nonmatch files*/
                        gGgrep.ReportingOptions |= MATCHENG_RPT_NONMATCH_FILES;
                        break;

                case 'M':
                        /*Marker character to delimit match/nonmatch in line*/
                        gGgrep.ReportingOptions |= MATCHENG_RPT_MARKER(*pOpt);
                        if (*pOpt++ == NUL) pOpt--;
                        break;

                case 'n':
                        /*Include file line number in display*/
                        gGgrep.ScanOptions |= SCANFILE_NUMBER_MATCHING_LINES;
                        gGgrep.ReportingOptions |= MATCHENG_RPT_LINENUMBER;
                        break;

                case 'O':
                        /*Configure optimisations (mainly for test rig)*/
                        GGrep_OptiOpts(pOpt);
                        continue;
                        break;

                case 'r':
                        /*Recurse down subdirectories*/
                        gGgrep.ScanOptions |= SCANFILE_OPT_RECURSE_DIRECTORIES;
                        break;

                case 'R':
                        /*Let CR serve as a line terminator*/
                        gGgrep.ScanOptions      |= SCANFILE_CR_IS_TERMINATOR;
                        gGgrep.ReportingOptions |= 
                                MATCHENG_RPT_REMOVE_TRAILING_CR;
                        break;

                case 'T':
                        /*Trace module operations according to flags*/
                        if (*pOpt == NUL) {
                                if (--argc == 0) {
                                        GGrep_Usage();
                                }
                                pOpt = *++argv;
                        }

                        /*Does the caller want details about Tracery?*/
                        if (pOpt[0] == '?') {
                                /*Yes, was Tracery compiled in?*/
                                #if ! TRACERY_ENABLED
                                        /*No, issue a warning*/
                                        fprintf(stderr, "%s: "
                                                "(Tracery not compiled in?)\n", 
                                                Platform_ProgramName());
                                #endif

                                /*Was a module name or wildcard given?*/
                                if (pOpt[1] == NUL) {
                                        /*No, request a brief summary*/
                                        Tracery_DisplayRegistrations(NULL);
                                } else {
                                        /*Yes, request full details*/
                                        Tracery_DisplayRegistrations(
                                                &pOpt[1]);
                                }
                        } else {
                                Tracery_Configure(pOpt);
                        }
                        continue;
                        /*break;*/

                case 'v':
                        /*Invert match sense*/
                        gGgrep.ReportingOptions |= 
                                MATCHENG_RPT_INVERT_MATCH_SENSE;
                        break;

                case 'V':
                        /*Display program version info*/
                        GGrep_Version();
                        break;

                case 'w':
                        /*Match edges must have nonword chars outside*/
                        /*Note: NOT equivalent to \<expression\>*/
                        gGgrep.RegexpConfig |= REGEXP_CONFIG_WORD_EDGES;
                        break;

                case 'W':
                        /*-w, plus constrain "." and "[^...]" to word chars*/
                        gGgrep.RegexpConfig |= REGEXP_CONFIG_WORD_EDGES | 
                                REGEXP_CONFIG_WORD_CONSTRAIN;
                        break;

                case 'x':
                        /*Only match full lines*/
                        gGgrep.RegexpConfig |= REGEXP_CONFIG_FULL_LINE_ONLY;
                        break;

                default:
                        fprintf(stderr, "ggrep: Unknown option: %c\n",
                                        pOpt[-1]);
                        GGrep_Usage();
                        break;

                }

                /*Have we reached the end of the word?*/
                if (*pOpt != NUL) {
                        /*No, handle next option within this word*/
                        goto DecodeOption;
                }

                /*Loop will consider next word*/

        }


        /*Did we get a RE spec while handling the option list?*/
        if (pRegexpText == NULL) {
                /*No, use next parameter as RE text*/
                pRegexpText = *argv++;
                argc--;

        }

        /*Convert ASCII RE description into compact form*/
        switch (gGgrep.Opt.SearchStyle) {
        case SEARCH_AS_EGREP:
                /*Extended REs: ? and + special, \? and \+ literals*/
                gGgrep.RegexpConfig |= 
                        REGEXP_CONFIG_PLUS | 
                        REGEXP_CONFIG_QUESTION_MARK | 
                        REGEXP_CONFIG_NAMED_CLASSES | 
                        REGEXP_CONFIG_ESC_LESS_THAN | 
                        REGEXP_CONFIG_ESC_GREATER_THAN | 
                        REGEXP_CONFIG_WORD_BOUNDARY | 
                        REGEXP_CONFIG_WORD_NONBOUNDARY |
                        REGEXP_CONFIG_OPS_LITERAL_START;

                /*Sadly, we only support a subset of extended RE syntax*/
                if (! RegExp_Compile(pRegexpText, 
                                     gGgrep.RegexpConfig, 
                                     &pCompactRE)) {
                        /*Found some fault during conversion*/
                        RegExp_Diagnose(NULL, Diagnosis);
                        fprintf(stderr, "ggrep: %s\n", Diagnosis);
                        return MAIN_RETURN_FAULT;
                }

                break;

        case SEARCH_AS_FGREP:
                /*Treat pattern as a simple string without special chars*/
                if (! RegExp_CompileString(pRegexpText, 
                                           gGgrep.RegexpConfig, 
                                           &pCompactRE)) {
                        /*Found some fault during conversion*/
                        RegExp_Diagnose(NULL, Diagnosis);
                        fprintf(stderr, "ggrep: %s\n", Diagnosis);
                        return MAIN_RETURN_FAULT;
                }
                break;

        case SEARCH_AS_GREP:
        case SEARCH_STYLE_UNSPECIFIED:
                /*Basic REs: ? and + literals, \? and \+ special*/
                gGgrep.RegexpConfig |= 
                        REGEXP_CONFIG_ESC_PLUS | 
                        REGEXP_CONFIG_ESC_QUESTION_MARK | 
                        REGEXP_CONFIG_NAMED_CLASSES | 
                        REGEXP_CONFIG_ESC_LESS_THAN | 
                        REGEXP_CONFIG_ESC_GREATER_THAN | 
                        REGEXP_CONFIG_WORD_BOUNDARY | 
                        REGEXP_CONFIG_WORD_NONBOUNDARY |
                        REGEXP_CONFIG_OPS_LITERAL_START;

                /*Compile RE*/
                if (! RegExp_Compile(pRegexpText, 
                                     gGgrep.RegexpConfig, 
                                     &pCompactRE)) {
                        /*Found some fault during conversion*/
                        RegExp_Diagnose(NULL, Diagnosis);
                        fprintf(stderr, "ggrep: %s\n", Diagnosis);
                        return MAIN_RETURN_FAULT;
                }
                break;

        }

        /*Are we displaying any lines?*/
        if (! (gGgrep.ReportingOptions & (MATCHENG_RPT_LINE | 
                                          MATCHENG_RPT_HIGHLIGHT))) {
                /*No, don't count lines (in case we were asked to)*/
                gGgrep.ScanOptions &= ~SCANFILE_NUMBER_MATCHING_LINES;
        }

        /*Does the client want to see the exact matching text?*/
        if (gGgrep.ReportingOptions & MATCHENG_RPT_HIGHLIGHT) {
                /*Yes, scanfile can't use approximations at start and end*/
                gGgrep.ScanOptions &= ~SCANFILE_OPT_APPROXIMATE;
        }

        /*Are we counting lines for an inverted match?*/
        if ((gGgrep.ScanOptions & SCANFILE_NUMBER_MATCHING_LINES) && 
            (gGgrep.ReportingOptions & MATCHENG_RPT_INVERT_MATCH_SENSE)) {
                /*Yes, be very specific so search can optimise properly*/
                gGgrep.ScanOptions &= ~SCANFILE_NUMBER_MATCHING_LINES;
                gGgrep.ScanOptions |= SCANFILE_NUMBER_NONMATCH_LINES;
        }

        /*Give ScanFile regular expression pattern plus search modifications*/
        if (! ScanFile_Pattern(pCompactRE, gGgrep.ScanOptions)) {
                /*ScanFile was unable to accept pattern for search*/
                fprintf(stderr, "ggrep: Unable to set up search?!\n");
                return MAIN_RETURN_FAULT;
        }

        /*Discard compact pattern since it isn't used again*/
        /*RegExp_Discard(pCompactRE);*/

        /*Remaining arguments (if any) are files to search*/

        /*Was the only file name "-"?*/
        if ((argc == 1) && (strcmp(*argv, "-") == 0)) {
                /*Yes, delete it so that stdin is selected*/
                argc--;
                argv++;
        }

        /*Were any files given?*/
        if (argc == 0) {
                /*No, immediately configure file scan for search*/
                ScanFile_Configure(gGgrep.ReportingOptions);

                /*Scan stdin using line-by-line search*/
                ScanFile_Search(NULL, NULL);

        } else {
                /*Was more than one file given (and names aren't suppressed?)*/
                if ((argc > 1) && (! SuppressFilename)) {
                        /*Yes, display filename as part of match*/
                        gGgrep.ReportingOptions |= MATCHENG_RPT_FILENAME;
                }

                /*Configure file scan for search*/
                ScanFile_Configure(gGgrep.ReportingOptions);

                /*Scan each file named on command line*/
                while (argc--) {
                        if (! ScanFile_Search(*argv++, NULL)) {
                                /*We may skip remaining files*/
                                break;
                        }
                }

        }

        /*Did no lines match (and no other errors detected)?*/
        if ((! ScanFile_MatchedAny()) && (gGgrep.ReturnCode == 0)) {
                /*Yes, report failure to environment*/
                gGgrep.ReturnCode = 1;
        }

        /*Report success or failure*/
        return gGgrep.ReturnCode;

} /*main*/