SMOLNET PORTAL home about changes
/* query.c -- apply a boolean query to a keyword file */
/* Bruce Tanner -- Cerritos College */

/* Version history:

   0.0 1993/06/18 Start the program
   0.1 1993/07/03 Finish coding
   0.8 1993/07/06 Squashed most of the bugs
   1.0 1993/07/13 Released
   1.1 1993/07/29 Changed grammar to allow Query inside Factor
   1.2 1993/07/29 Invoke interactive mode if argc < 4
   1.3 1993/08/04 Change name to query, calling it search is confusing
   1.4 1993/08/06 Move wildcard processing from shell to inside program
   1.5 1993/11/17 Include punctuation in query string to match build_index
   1.6 1993/12/01 Handle multiple topic field sizes
   1.7 1994/03/01 Fix bad reference to freed pointer
   1.7a 1994/06/29 Added some include files and related stuff for DECC. - FM
   1.8 1994/11/04 Handle host/port included in selector file
   1.9 1994/12/31 Trim off host/port from file_name in select_result(),
   		   and report any failure to access file_name. - FM

*/

/* Usage: search idx-file out-file query host port directory */

/* Query:  expr {expr}              implicit 'and' between expressions */
/* Expr:   term {or term}                                              */
/* Term:   factor {and|not factor}                                     */
/* Factor: (query) | token                                             */
/* Token:  [field name] word                                           */
/* Word:   a-z{a-z}[*]                                                 */


#include <ssdef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <rms.h>
#include <descrip.h>
#include <fscndef.h>
#include <unixlib.h>
#include <lib$routines.h>
#include <starlet.h>

#define KEY_NAME 32    /* maximum size of key name */
#define MAX_QUERY 500  /* maximum size of query */
#define SPEC_SIZE 256  /* file specification size */
#define DEBUG 0        /* perform debugging printfs */
#define MOD_REALLOC 1  /* handle initial realloc() problem */
#define MEM_DEBUG 0    /* realloc() debugging printfs */

typedef struct {
    int index;        /* selector index */
    int file;         /* file index */
} Select;

typedef struct {
    int count;         /* number of members in result set */
    Select *select;    /* set of selectors */
} Result;

typedef struct {
    int krf;           /* key-of-reference for this token */
    char *str;         /* token string */
} Token;


int query(char *, Result *, Token *);
void emit(Select, char*, char*);
void and(Result, Result, Result *);
void or(Result, Result, Result *);
void select_result(Result);
void *my_realloc(void *, int);

struct FAB idxfab, selfab;
struct RAB idxrab, selrab;
struct XABSUM xabsum;
FILE *outfile;
char *index_type = ".IDX", *selector_type = ".SEL";
int index_offset, keys, max_key = 0, last_field = 0;
char *index_field;
struct XABKEY **keytab;
int selector_index = 0;  /* selector file index */
char **selector_name;    /* array of selector file names */
int *index_size;         /* array of selector file index field sizes */

int main(int argc, char *argv[])
{
    int status, ind, size, RunQuery, context = 0;
    static char input_spec[SPEC_SIZE], idx_spec[SPEC_SIZE], file_name[SPEC_SIZE];
    char *cp, orig_qstr[MAX_QUERY], qstr[MAX_QUERY];
    Result file_result, result;
    Token token;
    struct fscndef scan_list[] = {{(short) 0, (short) FSCN$_NODE, (long) 0},
                                  {(short) 0, (short) FSCN$_DEVICE, (long) 0},
                                  {(short) 0, (short) FSCN$_DIRECTORY, (long) 0},
                                  {(short) 0, (short) FSCN$_NAME, (long) 0},
                                  {(short) 0, (short) FSCN$_VERSION, (long) 0},
                                  {(short) 0, (short) 0, (long) 0}};

    $DESCRIPTOR(input_dsc, input_spec);
    $DESCRIPTOR(idx_dsc, idx_spec);
    $DESCRIPTOR(file_name_dsc, file_name);

    RunQuery = (argc < 4);  /* verb & 2 args means interactive query */

    /* assume that the wrapper command file handles validation */
    if (!RunQuery && (argc != 6)) {
        printf("Usage: query idx-file out-file query-string host port\n");
        exit(1);
    }

    if (RunQuery && argc < 2) {
        argv[1] = (char *) malloc(80);
        printf("Index file to search: ");
        fgets(argv[1], 80, stdin);
        argv[1][strlen(argv[1]) - 1] = '\0';  /* remove \n */
    }

    if (RunQuery && argc < 3) {
        printf("Enter query: ");
        fgets(orig_qstr, MAX_QUERY, stdin);
        orig_qstr[strlen(orig_qstr) - 1] = '\0';  /* remove \n */
    }
    else if (argc == 3)
        strcpy(orig_qstr, argv[2]);
    else
        strcpy(orig_qstr, argv[3]);  /* make our copy of the query */

    for (cp = orig_qstr; *cp; cp++) {
/*
   Now that the indexer punctuation set is programmable,
   we can't afford to omit punctuation any more

        if (ispunct(*cp) && (*cp != '(') && (*cp != ')') && (*cp != '*'))
            *cp = ' ';    { convert punct. except '(' ')' '*' to spaces }
*/
        *cp = _tolower(*cp);  /* force word lowercase */
    }
    strcat(orig_qstr, " ");          /* query ends with a space */

    result.count = 0;           /* init result */
    result.select = NULL;
    keytab = (struct XABKEY **) 0;

    strcpy(input_spec, argv[1]);  /* set up descriptor to wildcard input spec */
    input_dsc.dsc$w_length = (short) strlen(input_spec);

    if (((status = sys$filescan(&input_dsc, scan_list, 0)) & 1) != SS$_NORMAL)
        lib$stop(status);

    cp = NULL;
    size = 0;
    for (ind = 0; ind < 4; ind++) {
        if (cp == NULL)
            cp = (char *) scan_list[ind].fscn$l_addr;
        size += scan_list[ind].fscn$w_length;
    }

    strncpy(idx_spec, cp, size);       /* copy node, dev, dir, name */
    idx_spec[size] = '\0';
    strcat(idx_spec, index_type);      /* add .idx */
    idx_dsc.dsc$w_length = (short) strlen(idx_spec);

    while (((status = lib$find_file(&idx_dsc, &file_name_dsc, &context, 0, 0, 0, 0))
           & 1) == SS$_NORMAL) {       /* while lib$find_file finds file names */

        if (DEBUG)
            printf("Find_file returned %s\n", file_name);

        cp = strchr(file_name, ' ');
        if (cp) *cp = '\0';            /* chop off trailing spaces */
        
        /* save the file names for when we need to get the selectors */
        selector_name = (char **) my_realloc((char **) selector_name,
                                            (++selector_index + 1) * sizeof(char *));
        selector_name[selector_index] = (char *) calloc(strlen(file_name) + 1,
                                                        sizeof(char));
        cp = strrchr(file_name, '.');
        if (cp) *cp = '\0';           /* once again throw out file type */

        strcpy(selector_name[selector_index], file_name);
        if (DEBUG)
            printf("Saving selector index %d = %s\n", selector_index,
                   selector_name[selector_index]);
        
        /* initialize index fab and rab */
        idxfab = cc$rms_fab;
        idxrab = cc$rms_rab;
        xabsum = cc$rms_xabsum;
        
        idxfab.fab$l_fna = file_name;
        idxfab.fab$b_fns = strlen(file_name);
        idxfab.fab$l_dna = index_type;
        idxfab.fab$b_dns = strlen(index_type);
        idxfab.fab$b_shr = FAB$M_SHRGET;
        idxfab.fab$l_xab = (char *) &xabsum;
        
        idxrab.rab$l_fab = (struct FAB *) &idxfab;
        idxrab.rab$b_rac = RAB$C_KEY;
        
        /* open index file */
        if (((status = sys$open(&idxfab)) & 1) != SS$_NORMAL)
            lib$stop(status);
        
        if (idxfab.fab$b_org != FAB$C_IDX) {
            printf("Idx file must be indexed\n");
            exit(1);
        }
        
        keys = xabsum.xab$b_nok;
        if (DEBUG)
            printf("Number of keys = %d\n", keys);
        
        keytab = (struct XABKEY **) my_realloc((struct XABKEY **) keytab,
                                              keys * sizeof(struct XABKEY **));
        
        /* build an array of XABKEY */
        for (ind = 0; ind < keys; ind++) {
            keytab[ind] = (struct XABKEY *)
                          my_realloc((struct XABKEY *) keytab[ind], 
                                     sizeof(struct XABKEY));
            *keytab[ind] = cc$rms_xabkey;
            keytab[ind]->xab$l_knm = (char *) my_realloc((char *)
                                                         keytab[ind]->xab$l_knm,
                                                         KEY_NAME + 1);
            strncpy((char *) keytab[ind]->xab$l_knm, "", KEY_NAME + 1);
            if (ind > 0) keytab[ind - 1]->xab$l_nxt = (char *) keytab[ind];
        }
        idxfab.fab$l_xab = (char *) keytab[0];
        
        /* fill in the key XABs */
        if (((status = sys$display(&idxfab)) & 1) != SS$_NORMAL)
            lib$stop(status);
        
        index_size = (int *) my_realloc((int *) index_size,
                                        (selector_index + 1) * sizeof(int));

        if (keys == 1) {
            index_size[selector_index] = idxfab.fab$w_mrs - keytab[0]->xab$b_siz0;
            index_offset = keytab[0]->xab$w_pos0 + keytab[0]->xab$b_siz0 -
                           index_size[selector_index];
            index_field = (char *) my_realloc((char *) index_field,
                                   (index_size[selector_index] + 1) * sizeof(char));
            strncpy(index_field, "", index_size[selector_index] + 1);
        }
        else   /* multi-key records don't have counts */
            index_size[selector_index] = 0;

        if (DEBUG)
            printf("Selector %d index size = %d\n", selector_index,
                   index_size[selector_index]);

        for (ind = 0; ind < keys; ind++) {
            if (DEBUG)
                printf("key %d: size = %d name = %s\n",
                       ind, 
                       keys == 1 ? keytab[ind]->xab$b_siz0 - 
                                   index_size[selector_index] :
                                   keytab[ind]->xab$b_siz0,
                                   keytab[ind]->xab$l_knm);
             max_key = max_key < keytab[ind]->xab$b_siz0 ?
                       keytab[ind]->xab$b_siz0 : max_key;
        }
        
        /* make the index file record */
        idxrab.rab$w_usz = idxfab.fab$w_mrs;
        idxrab.rab$l_ubf = (char *) my_realloc((char *) idxrab.rab$l_ubf,
                                    (idxrab.rab$w_usz + 1) * sizeof(char));
        strncpy(idxrab.rab$l_ubf, "", idxrab.rab$w_usz + 1);
        
        /* connect record streams */
        if (((status = sys$connect(&idxrab)) & 1) != SS$_NORMAL)
            lib$stop(status);
        
        strcpy(qstr, orig_qstr);            /* query and friends mangle qstr */
        query(qstr, &file_result, &token);  /* evaluate the query on this file */
        or(result, file_result, &result);   /* accumulate results */
        
        status = sys$close(&idxfab);
    }  /* while finding input files */
    
    if (RunQuery)
        select_result(result);
    else {
        /* open the output file and write the resulting selector set */
        outfile = fopen(argv[2], "a");
        if (outfile == NULL) {
            perror("Output file could not be opened");
            exit(1);
        }
        for (ind = 0; ind < result.count; ind++)
            emit(result.select[ind], argv[4], argv[5]);
        fclose(outfile);
    }
    status = sys$close(&selfab);
}


void open_selector(char *file_name)
{
    int status;

    /* initialize selector fab and rab */
    selfab = cc$rms_fab;
    selrab = cc$rms_rab;

    selfab.fab$l_fna = file_name;
    selfab.fab$b_fns = strlen(file_name);
    selfab.fab$l_dna = selector_type;
    selfab.fab$b_dns = strlen(selector_type);
    selfab.fab$b_shr = FAB$M_SHRGET;

    selrab.rab$l_fab = (struct FAB *) &selfab;
    selrab.rab$b_rac = RAB$C_KEY;

    /* open selector file */
    if (((status = sys$open(&selfab)) & 1) != SS$_NORMAL)
        lib$stop(status);

    if (selfab.fab$b_org != FAB$C_IDX) {
        printf("Selector file must be indexed\n");
        exit(1);
    }

    /* make the selector file record */
    selrab.rab$w_usz = selfab.fab$w_mrs;
    selrab.rab$l_ubf = (char *) my_realloc((char *) selrab.rab$l_ubf,
                                           (selrab.rab$w_usz + 1) * sizeof(char));
    strncpy(selrab.rab$l_ubf, "", selrab.rab$w_usz + 1);

    if (((status = sys$connect(&selrab)) & 1) != SS$_NORMAL)
        lib$stop(status);
}


/* return the key of reference associated with the field name */ 
int is_field(char *str, int *krf)
{
    int ind;

    for (ind = 0; ind < keys; ind++)
        if (strcmp(str, keytab[ind]->xab$l_knm) == 0) {
            *krf = last_field = ind;  /* field name matched */
            return (1);
        }
    *krf = last_field;   /* not a field name, carry forward last field */
    return 0;
}


/* return the next token or field name (key of reference) and token */
void get_token(char *qstr, Token *token)
{
    char *str = NULL, *cp;
    int krf = -1;

    if (DEBUG)
        printf("Token: '%s'  ", qstr);
    while (*qstr && (*qstr <= ' '))  /* remove leading spaces and junk */
        strcpy(qstr, qstr + 1);

    if ((*qstr == '(') || (*qstr == ')')) {
        str = (char *) calloc(2, sizeof(char));
        strncpy(str, qstr, 1);
        strcpy(qstr, qstr + 1);
    }
    else {
        if (strchr(qstr, ' ') && strchr(qstr, ')'))
            cp = strchr(qstr, ' ') < strchr(qstr, ')') ?
                 strchr(qstr, ' ') : strchr(qstr, ')');
        else if (strchr(qstr, ')') == NULL)
            cp = strchr(qstr, ' ');
        else if (strchr(qstr, ' ') == NULL)
            cp = strchr(qstr, ')');
        else
            cp = NULL;
        if (cp) {
            str = (char *) calloc(max_key + 1, sizeof(char));
            strncpy(str, qstr, cp - qstr);
            if (*cp == ')')
                strcpy(qstr, cp);
            else
                strcpy(qstr, cp + 1);
            if (is_field(str, &krf)) {
                free(str);
                if (strchr(qstr, ' ') && strchr(qstr, ')'))
                    cp = strchr(qstr, ' ') < strchr(qstr, ')') ?
                         strchr(qstr, ' ') : strchr(qstr, ')');
                else if (strchr(qstr, ')') == NULL)
                    cp = strchr(qstr, ' ');
                else if (strchr(qstr, ' ') == NULL)
                    cp = strchr(qstr, ')');
                else
                    cp = NULL;
                str = (char *) calloc(keytab[krf]->xab$b_siz0 + 1, sizeof(char));
                strncpy(str, qstr, cp - qstr);
                if (*cp == ')')
                    strcpy(qstr, cp);
                else
                    strcpy(qstr, cp + 1);
            }
        }
        else
            str = (char *) calloc(1, sizeof(char));
    }
    if (DEBUG)
        printf("Returns: '%s' Op: '%s' %d\n", qstr, str, krf);
    token->str = str;
    token->krf = krf;
}


/*
   Lance was right, realloc sometimes blows when initially allocating memory
   MOD_REALLOC indicates whether to use malloc() on initial allocation
 */
void *my_realloc(void *mem, int size)
{
    void *mem_ptr;

    if ((mem == (void *) 0) && (MOD_REALLOC))
        return ((void *) malloc(size));
    else {
        if (MEM_DEBUG)
            printf("Called realloc(%X, %d)\n", mem, size);
        mem_ptr = (void *) realloc(mem, size);
        if (MEM_DEBUG)
            printf("Realloc returned %X\n", mem_ptr);
        return (mem_ptr);
    }
}


/* create a set of selectors that are associated with the token */
void find(Token token, Result *rx)
{
    int status, ind, value;

    rx->count = 0;        /* assume no match */
    rx->select = NULL;

    idxrab.rab$b_rac = RAB$C_KEY;
    idxrab.rab$b_krf = token.krf;
    idxrab.rab$l_kbf = token.str;
    idxrab.rab$l_rop = 0;            /* set up exact match */
    idxrab.rab$b_ksz = keys == 1 ? keytab[token.krf]->xab$b_siz0 -
                                   index_size[selector_index] :
                                   keytab[token.krf]->xab$b_siz0;
    if (token.str[strlen(token.str) - 1] == '*') {
        idxrab.rab$b_ksz = strlen(token.str) - 1;
        idxrab.rab$l_rop = RAB$M_KGE;  /* set up approximate generic match */
    }
    /* key can't be shorter than field size */
    while (strlen(token.str) < idxrab.rab$b_ksz)
        strcat(token.str, " ");

    /* find the start record */
    if (((status = sys$find(&idxrab)) & 1) != SS$_NORMAL)
        return;  /* no match */

    idxrab.rab$b_rac = RAB$C_SEQ;
    while (((status = sys$get(&idxrab)) & 1) == SS$_NORMAL) {
        if (strncmp((char *) (idxrab.rab$l_ubf + keytab[token.krf]->xab$w_pos0),
                    token.str, idxrab.rab$b_ksz) != 0)
            break;  /* no match */

        if (keys == 1) {
            strncpy(index_field,
                    (char *) (idxrab.rab$l_ubf + index_offset),
                    index_size[selector_index]);
            value = atoi(index_field);
        }
        /* else handle multi-key rfa here */

        for (ind = 0; ind < rx->count; ind++)
            if ((rx->select[ind].index == value) &&
                (rx->select[ind].file == selector_index))  /* if the value already there */
                break;                        /* don't add it */
        /* unfortunately, you can't put a 'continue' in the previous line */
        if ((ind < rx->count) && (rx->select[ind].index == value) &&
            (rx->select[ind].file == selector_index))
            continue;

        rx->select = (Select *) my_realloc((Select *) rx->select,
                                           (rx->count + 1) * sizeof(Select));

        /* keep the values in ascending order */
        for (ind = rx->count; ind >= 0; ind--)
            if ((ind == 0) ||
                (rx->select[ind - 1].file < selector_index) ||
                ((rx->select[ind - 1].file == selector_index) &&
                 (rx->select[ind - 1].index < value))) {
                rx->select[ind].file = selector_index;
                rx->select[ind].index = value;
                break;
            }
            else
                rx->select[ind] = rx->select[ind - 1];

        rx->count++;
    }
    if (DEBUG) {
        printf("Find: %s -> ", token.str);
        for (ind = 0; ind < rx->count; ind++)
            printf("%d-%d ", rx->select[ind].file, rx->select[ind].index);
        printf("\n");
    }
}


/* selector booleans */
int select_lt(Select s1, Select s2)
{
    return ((s1.file < s2.file) ||
            ((s1.file == s2.file) && (s1.index < s2.index)));
}


int select_eq(Select s1, Select s2)
{
    return ((s1.file == s2.file) && (s1.index == s2.index));
}


/* perform set intersection */
void and(Result r1, Result r2, Result *r3)
{
    int ind1 = 0, ind2 = 0;
    Result rx;
                  
    rx.count = 0;
    rx.select = NULL;

    if (DEBUG) {
        int ind;
        printf("R1: ");
        for (ind = 0; ind < r1.count; ind++)
            printf("%d-%d ", r1.select[ind].file, r1.select[ind].index);
        printf("\n");
        printf("R2: ");
        for (ind = 0; ind < r2.count; ind++)
            printf("%d-%d ", r2.select[ind].file, r2.select[ind].index);
        printf("\n");
    }

    for (;;) {
        if ((ind1 == r1.count) || (ind2 == r2.count))
            break;
        else if (select_lt(r1.select[ind1], r2.select[ind2])) ind1++;
        else if (select_lt(r2.select[ind2], r1.select[ind1])) ind2++;
        else if (select_eq(r1.select[ind1], r2.select[ind2])) {
            rx.select = (Select *) my_realloc((Select *) rx.select,
                                              (rx.count + 1) * sizeof(Select));
            rx.select[rx.count].file = r1.select[ind1].file;
            rx.select[rx.count].index = r1.select[ind1].index;
            rx.count++;
            ind1++; ind2++;
        }
    }
    if (r3->select) free(r3->select);
    *r3 = rx;

    if (DEBUG) {
        int ind;
        printf("AND: ");
        for (ind = 0; ind < rx.count; ind++)
            printf("%d-%d ", rx.select[ind].file, rx.select[ind].index);
        printf("\n");
     }
}


/* perform set inclusion */
void or(Result r1, Result r2, Result *r3)
{
    int ind1 = 0, ind2 = 0;
    Result rx;
                  
    rx.count = 0;
    rx.select = NULL;

    if (DEBUG) {
        int ind;
        printf("R1: ");
        for (ind = 0; ind < r1.count; ind++)
            printf("%d-%d ", r1.select[ind].file, r1.select[ind].index);
        printf("\n");
        printf("R2: ");
        for (ind = 0; ind < r2.count; ind++)
            printf("%d-%d ", r2.select[ind].file, r2.select[ind].index);
        printf("\n");
    }

    for (;;) {
        if ((ind1 == r1.count) && (ind2 == r2.count))
            break;
        else if ((ind2 == r2.count) ||
                 ((ind1 < r1.count) &&
                  select_lt(r1.select[ind1], r2.select[ind2]))) {
            rx.select = (Select *) my_realloc((Select *) rx.select,
                                              (rx.count + 1) * sizeof(Select));
            rx.select[rx.count].file = r1.select[ind1].file;
            rx.select[rx.count].index = r1.select[ind1].index;
            rx.count++;
            ind1++;
        }
        else if ((ind1 == r1.count) ||
                 ((ind2 < r2.count) &&
                  select_lt(r2.select[ind2], r1.select[ind1]))) {
            rx.select = (Select *) my_realloc((Select *) rx.select,
                                              (rx.count + 1) * sizeof(Select));
            rx.select[rx.count].file = r2.select[ind2].file;
            rx.select[rx.count].index = r2.select[ind2].index;
            rx.count++;
            ind2++;
        }
        else if (select_eq(r1.select[ind1], r2.select[ind2])) {
            rx.select = (Select *) my_realloc((Select *) rx.select,
                                              (rx.count + 1) * sizeof(Select));
            rx.select[rx.count].file = r1.select[ind1].file;
            rx.select[rx.count].index = r1.select[ind1].index;
            rx.count++;
            ind1++; ind2++;
        }
    }

    if (r3->select) free(r3->select);
    *r3 = rx;

    if (DEBUG) {
        int ind;
        printf("OR: ");
        for (ind = 0; ind < rx.count; ind++)
            printf("%d-%d ", rx.select[ind].file, rx.select[ind].index);
        printf("\n");
     }
}


/* perform set exclusion */
void not(Result r1, Result r2, Result *r3)
{
    int ind1 = 0, ind2 = 0;
    Result rx;
                  
    rx.count = 0;
    rx.select = NULL;

    if (DEBUG) {
        int ind;
        printf("R1: ");
        for (ind = 0; ind < r1.count; ind++)
            printf("%d-%d ", r1.select[ind].file, r1.select[ind].index);
        printf("\n");
        printf("R2: ");
        for (ind = 0; ind < r2.count; ind++)
            printf("%d-%d ", r2.select[ind].file, r2.select[ind].index);
        printf("\n");
    }

    for (;;) {
        if (ind1 == r1.count)
            break;
        else if ((ind2 == r2.count) ||
                 select_lt(r1.select[ind1], r2.select[ind2])) {
            rx.select = (Select *) my_realloc((Select *) rx.select,
                                              (rx.count + 1) * sizeof(Select));
            rx.select[rx.count].file = r1.select[ind1].file;
            rx.select[rx.count].index = r1.select[ind1].index;
            rx.count++;
            ind1++;
        }
        else if (select_lt(r2.select[ind2], r1.select[ind1])) ind2++;
        else if (select_eq(r1.select[ind1], r2.select[ind2])) {
            ind1++; ind2++;
        }
    }
    if (r3->select) free(r3->select);
    *r3 = rx;

    if (DEBUG) {
        int ind;
        printf("NOT: ");
        for (ind = 0; ind < rx.count; ind++)
            printf("%d-%d ", rx.select[ind].file, rx.select[ind].index);
        printf("\n");
     }
}


/* parse factor */
int factor(char *qstr, Result *result, Token *op)
{
    Token token;

    if (DEBUG)
        printf("Factor\n");

    get_token(qstr, &token);
    if (strcmp(token.str, "(") == 0) {
        if (!query(qstr, result, op)) {
            if (DEBUG)
                printf("Query failed; exit Factor with failure\n");
            return (0);
        }
        if (strcmp(op->str, ")") != 0) {
            if (DEBUG)
                printf("Query success, no rparen; exit Factor with failure\n");
            return (0);
        }
    }
    else
        find(token, result);

    free(token.str);
    get_token(qstr, op);
    if (DEBUG)
        printf("Exit Factor\n");
    return (1);
}


/* parse term */
int term(char *qstr, Result *result, Token *op)
{
    Result temp;
    Token token;

    if (DEBUG)
        printf("Term\n");

    if (factor(qstr, result, op)) {
        while ((strcmp(op->str, "and") == 0) || (strcmp(op->str, "not") == 0)) {
            if (!factor(qstr, &temp, &token))
                return (0);
            if (strcmp(op->str, "and") == 0)
                and(*result, temp, result);
            else if (strcmp(op->str, "not") == 0)
                not(*result, temp, result);
            free(op->str);
            *op = token;
            free(temp.select);
        }
        if (DEBUG)
            printf("Exit Term\n");
        return (1);
    }
    if (DEBUG)
        printf("Exit Term with failure\n");
    return (0);
}


/* parse expression */
int expr(char *qstr, Result *result, Token *op)
{
    Result temp;
    Token token;

    if (DEBUG)
        printf("Expr\n");

    if (term(qstr, result, op)) {
        while (strcmp(op->str, "or") == 0) {
            if (!term(qstr, &temp, &token))
                return (0);
            or(*result, temp, result);
            free(op->str);
            *op = token;
            free(temp.select);
        }
        if (DEBUG)
            printf("Exit Expr\n");
        return (1);
    }
    if (DEBUG)
        printf("Exit Expr with failure\n");
    return (0);
}

/* parse query */
int query(char *qstr, Result *result, Token *op)
{
    Result result2;
    char temp_qstr[MAX_QUERY];
    int status;

    if (DEBUG)
        printf("Query\n");

    status = expr(qstr, result, op);      /* evaluate the expr */
    while ((strlen(op->str) > 0) &&       /* non-boolean at end of expression */
           (strcmp(op->str, ")") != 0)) { /* and it isn't a ")" */
        if (DEBUG)
            printf("Found \"%s\" at end of expression; 'and' assumed\n", op->str);
        strcpy(temp_qstr, op->str);       /* put token */
        strcat(temp_qstr, " ");           /* back on the */
        strcat(temp_qstr, qstr);          /* front of the query */
        strcpy(qstr, temp_qstr);
        free(op->str);
        status = expr(qstr, &result2, op);
        and(*result, result2, result);    /* 'and' expressions together */
        free(result2.select);
    }
    free(op->str);
    if (DEBUG)
        printf("Exit Query\n");

    return (status);
}


/* read selector record */
void find_selector(Select sel)
{
    int status;
    static int current_file = 0;

    sprintf(index_field, "%0*d", index_size[sel.file], sel.index);

    if (sel.file != current_file) {
        current_file = sel.file;
        status = sys$close(&selfab);
        open_selector(selector_name[current_file]);
    }
    selrab.rab$b_rac = RAB$C_KEY;
    selrab.rab$b_krf = 0;
    selrab.rab$l_kbf = index_field;
    selrab.rab$l_rop = 0;            /* set up exact match */
    selrab.rab$b_ksz = index_size[sel.file];

    /* find the selector record */
    if (((status = sys$get(&selrab)) & 1) != SS$_NORMAL)
        lib$stop(status);

    * (char *) (selrab.rab$l_ubf + selrab.rab$w_rsz) = '\0'; /* terminate string */
}


/* lookup selector with key 'index'; print selector, host, port */
void emit(Select sel, char *host, char *port)
{
    int tabs = 0;
    char *ptr;

    if (DEBUG)
        printf("Selector %d\n", sel.index);

    find_selector(sel);

    /*
       this assumes that the first 'index_size' characters of the selector 
       record is the selector index field and the rest is the selector itself
    */

    for (ptr = (char *) (selrab.rab$l_ubf + index_size[sel.file]); *ptr; ptr++)
        if (*ptr == '\t') tabs++;    /* count tabs in selector */

    if (tabs == 1)  /* just title <tab> selector; add host and port */
        fprintf(outfile, "%s\t%s\t%s\n", (char *) (selrab.rab$l_ubf +
                index_size[sel.file]), host, port);
    else            /* host and port already in selector */
        fprintf(outfile, "%s\n", (char *) (selrab.rab$l_ubf +
                index_size[sel.file]));
}


void select_result(Result result)
{
    int ind, start, end;
    char *ptr1, *ptr2, file_name[SPEC_SIZE], *cp;
    char gtype, inputline[SPEC_SIZE], answer[20];
    FILE *fp = NULL;

    printf("There are %d topics found:\n", result.count);

    /* list all the selectors */
    for (ind = 0; ind < result.count; ind++) {
        find_selector(result.select[ind]);
        ptr1 = (char *) (selrab.rab$l_ubf +
			 index_size[result.select[ind].file] + 1);
        ptr2 = strchr(ptr1, '\t');
        *ptr2 = '\0';
        printf ("%d.  %s\n", ind + 1, ptr1);
    }

    /* display the topics selected */
    for (;;) {
        do {
            ind = 0;  /* if non-numeric, ind will stay 0 */
            printf("\nSelect topic to view [0 to quit]: ");
            fgets(answer, 10, stdin);
            sscanf(answer, "%d", &ind);
        } while ((ind < 0) || (ind > result.count));
        if (ind == 0)
            return;

        find_selector(result.select[ind - 1]);
        ptr1 = (char *) (selrab.rab$l_ubf +
			 index_size[result.select[ind-1].file] + 1);
        ptr2 = strchr(ptr1, '\t');
	if ((gtype = *(ptr2+1)) == 'R') {
	    /* Section from a text database */
            sscanf(ptr2 + 2, "%d-%d-%s", &start, &end, file_name);
	    /* Trim off host and port if present */
	    if ((cp=strchr(file_name, '\t')) != NULL)
	    	*cp = '\0';
            if ((fp = fopen(file_name, "r", "shr=get", "mbc=32")) != NULL) {
                fseek(fp, start, SEEK_SET);
                printf("\n\nThis is from the document %s\n\n", file_name);
                while (fgets(inputline, sizeof(inputline), fp) != NULL) {
                    printf("%s", inputline);
	            if (ftell(fp) >= end)
                        break;
	        }
	    } else {
	        printf("\n\nCould not access the document %s\n", file_name);
	    }
	} else if (gtype == '0') {
	    /* Whole text file */
	    strcpy(file_name, ptr2+2);
	    /* Trim off host and port if present */
	    if ((cp=strchr(file_name, '\t')) != NULL)
	    	*cp = '\0';
	    if ((fp = fopen(file_name, "r", "shr=get", "mbc=32")) != NULL) {
                while (fgets(inputline, sizeof(inputline), fp) != NULL)
                    printf("%s", inputline);
	    } else {
	        printf("\n\nCould not access the document %s\n", file_name);
	    }
	} else {
	    /* Binary or other, non-text/plain file */
	    strcpy(file_name, ptr2+2);
	    /* Trim off host and port if present */
	    if ((cp=strchr(file_name, '\t')) != NULL)
	    	*cp = '\0';
	    printf("\n\nThe document %s is not plain text.\n", file_name);
	}
        if (fp)
	    fclose(fp);
    }
}

.
Response: text/plain
Original URLgopher://bitreich.org/0/gopher2007/2007-gopher-mirror/gop...
Content-Typetext/plain; charset=utf-8