SMOLNET PORTAL home about changes
/* qi_query - module for query command */
/* Bruce Tanner - Cerritos College */


/* 1993/09/14 - JLW@psulias.psu.edu fixed misextracted ATTR byte in display_id()
                                    that caused sequence numbers to spill over
                                    into ATTR                 */
 
#include stdio
#include string
#include ctype
#include ssdef
#include descrip
#include rms
#include psldef
#include "qi.h"

struct FAB idxfab, datfab;
struct RAB idxrab, datrab;
struct XABKEY idxxab, datxab;
char idx_record[IDX_RECORD_SIZE + 1];
char idx_key[IDX_KEY_SIZE + 1];
char dat_record[DAT_RECORD_SIZE + 1];
char dat_key[DAT_KEY_SIZE + 1];
int db_status;

typedef struct rstruct {
    int id, field;
} Result;

#define NotRet(x) ((x->type & TYPE_RETURN) == 0)

void writestring(int, char *);
void swrite(int, char *, ...);
char *new_string(char *);
Arg *make_arg(char *, int, char *, int);
void free_args(Arg *);
void qilog(int, char *, ...);
void *my_realloc(void *, int);
char *getlogical(char *);

extern Fields fields[];
extern int mode;


char *get_value(int sock, char *key, char *in_field, char *out_field)
{
    char *cp, temp[KEYWORD_SIZE + 1];
    int status;

    idxrab.rab$b_rac = RAB$C_KEY;
    idxrab.rab$l_rop = 0;            /* set up exact match */
    idxrab.rab$b_ksz = KEYWORD_SIZE + FIELD_SIZE;
    if (cp = strchr(key, ' '))
        *cp = '\0';                  /* strip trailing spaces */
    strcpy(temp, key);
    for (cp = temp; *cp; cp++)
        *cp = _tolower(*cp);         /* force lowercase */
    sprintf(idx_key, "%-*s%s", KEYWORD_SIZE, temp, in_field);

    status = sys$get(&idxrab);
    if ((status & 1) == 0) {
        if (DEBUG)
            swrite(sock, "get_value doesn't find key %s\r\n", idx_key);
        return NULL;
    }
    if (DEBUG)
        swrite(sock, "get_value finds key %s\r\n", idx_record);

    sprintf(dat_key, "%s%s%0*d", idx_record + KEYWORD_SIZE + FIELD_SIZE,
            out_field, SEQ_SIZE, 0);
    idxrab.rab$b_rac = RAB$C_KEY;
    idxrab.rab$l_rop = 0;
    idxrab.rab$b_ksz = strlen(dat_key);
    if (DEBUG)
        swrite(sock, "get_value lookup %s\r\n", dat_key);

    status = sys$get(&datrab);
    if ((status & 1) == 0) {
        if (DEBUG)
            swrite(sock, "get_value doesn't find field %s, status %d\r\n",
                   out_field, status);
        return NULL;
    }
    dat_record[datrab.rab$w_rsz] = '\0';  /* terminate string */   
    if (DEBUG)
        if (strcmp(out_field, PASSWORD_FIELD))
            swrite(sock, "get_value finds field %s value %s\r\n",
                   out_field, dat_record);
        else
            swrite(sock, "get_value finds password but I'm not going to show it to you.\r\n");
    return (dat_record + ID_SIZE + FIELD_SIZE + SEQ_SIZE + ATTR_SIZE);
}


int find_exact(int sock, char *query, int field)
{
    char *cp;
    int status;

    idxrab.rab$b_rac = RAB$C_KEY;
    idxrab.rab$l_rop = 0;            /* set up exact match */
    idxrab.rab$b_ksz = KEYWORD_SIZE + FIELD_SIZE;
    if (cp = strchr(query, ' '))
        *cp = '\0';                  /* strip trailing spaces */
    if (field == atoi(SOUNDEX_FIELD))         /* soundex field means convert */
        soundex(query, query, SOUNDEX_SIZE);  /* and store soundex in query */

    sprintf(idx_key, "%-*s%0*d", KEYWORD_SIZE, query,
            FIELD_SIZE, field);

    status = sys$get(&idxrab);
    if (DEBUG)
        swrite(sock, "exact match returns %d\r\n", status);
    return (status & 1);
}


/* find index that starts with the query string */
int find_approx(int sock, char *query, int field)
{
    int status;

    idxrab.rab$b_rac = RAB$C_KEY;
    idxrab.rab$l_rop = RAB$M_KGE;     /* set up approximate generic match */
    idxrab.rab$b_ksz = strlen(query); /* actual key size */
    sprintf(idx_key, "%-*s", KEYWORD_SIZE, query);

    status = sys$get(&idxrab);
    if ((status & 1) == 0) {
        if (DEBUG)
            swrite(sock,"approx match returns %d\r\n", status);
        return status;
    }
    /* this sould always find something, is it the right record? */
    status = (strncmp(idx_record, query, strlen(query)) == 0);
    if (DEBUG)
        swrite(sock, "approx match finds %s and returns %d\r\n",
                idx_record, status);
    return status;
}
            

/* find soundex match */
int find_soundex(int sock, char *query, int *field)
{
    int status;

    /* ensure only name field is implicitely searched for soundex */
    if (*field != atoi(NAME_FIELD)) {
        if (DEBUG)
            swrite(sock, "soundex match rejects field %d\r\n", *field);
        return;
    }

    *field = atoi(SOUNDEX_FIELD);
    idxrab.rab$b_rac = RAB$C_KEY;
    idxrab.rab$l_rop = 0;            /* set up exact match */
    idxrab.rab$b_ksz = KEYWORD_SIZE + FIELD_SIZE;
    sprintf(idx_key, "%-*s%s", KEYWORD_SIZE,
            soundex(query, query, SOUNDEX_SIZE),
            SOUNDEX_FIELD);

    status = sys$get(&idxrab);
    if (DEBUG)
        swrite(sock, "soundex match returns %d\r\n", status);
    return (status & 1);
}


int find(int sock, Arg *arg, Result **result, int size)
{
    int status, id, field;
    char *cp, query[DATA_SIZE + 1], wild_query[DATA_SIZE + 1];
    char found_keyword[KEYWORD_SIZE + 1], found_id[ID_SIZE + 1];
    char found_field[FIELD_SIZE + 1];
    Result *rptr, *pptr;
    $DESCRIPTOR(wild_dsc, wild_query);
    $DESCRIPTOR(found_dsc, found_keyword);
 
    /* find() only works on indexed fields */
    if ((fields[arg->field].attrib & ATTR_INDEXED) == 0)
        return size;

    if (DEBUG)
        swrite(sock, "Find %s in field %d\r\n", arg->value, arg->field);

    strcpy(query, arg->value);
    wild_query[0] = '\0';
    field = arg->field;

    for (cp = query; *cp; cp++)              /* convert all '?' to '%' */
        if (*cp == '?')                      /* STR$WILDCARD uses '%' */
            *cp = '%';
    if ((cp = strchr(query, '*')) || (cp = strchr(query, '%'))) {  /* wildcard? */
        strcpy(wild_query, query);           /* make a copy with the wildcard */
        *cp = '\0';                          /* truncate at the wildcard */
        if (!find_approx(sock, query, field)) /* try to find the first part */
            return size;                     /* no match */
    }
    else
        if (!find_exact(sock, query, field))     /* no wildcard, find the item */
            if (!EXACT && !find_approx(sock, query, field)) /* no exact match, try approx match */
                if (!find_soundex(sock, query, &field)) /* no approx, try soundex */
                    return size;             /* no match */

 
    idxrab.rab$b_rac = RAB$C_SEQ;
    do {
        strncpy(found_keyword, idx_record, KEYWORD_SIZE);
        found_keyword[KEYWORD_SIZE] = '\0';
        if (cp = strchr(found_keyword, ' ')) *cp = '\0';
        if ((idxrab.rab$l_rop == 0) &&   /* if exact match, */
            strcmp(found_keyword, query)) /* do exact compare */
            break;  /* no match */
        if ((idxrab.rab$l_rop == RAB$M_KGE) &&   /* if approx match, */
            strncmp(found_keyword, query, strlen(query))) /* approx compare */
            break;  /* no match */
        if (strlen(wild_query)) {   /* if wildcard match, */
            found_dsc.dsc$w_length = (short) strlen(found_keyword);
            wild_dsc.dsc$w_length = (short) strlen(wild_query);
            if ((str$match_wild(&found_dsc, &wild_dsc) & 1) == 0)  /* wild compare */
                continue;  /* no match, try again */
        }
        sprintf(found_field, "%0*d", FIELD_SIZE, field);
        if (strncmp(found_field, idx_record + KEYWORD_SIZE, FIELD_SIZE) != 0)
            continue;  /* this isn't the field we're looking for */
        strncpy(found_id, idx_record + KEYWORD_SIZE + FIELD_SIZE, ID_SIZE);
        found_id[ID_SIZE] = '\0';
        if (DEBUG)
            swrite(sock, "-100: Find >> %s\r\n", idx_record);
        *result = (Result *) my_realloc((Result *) *result, (size + 1) * sizeof(Result));
        id = atoi(found_id);
        (*result)[size].id = id;
        (*result)[size].field = atoi(found_field);
        size++;
    } while (((status = sys$get(&idxrab)) & 1) == SS$_NORMAL);

    return size;
}


/* this routine handles the rare case where a name= query
   matches both name and nickname (inflating the match count)
   and another indexed query has no matches;
   thus a false matched == iq in resolve()
*/
int validate_match(Result *results, int end, int matched, Arg *list)
{
    Arg *ptr;
    int ind;

    /* look for an indexed query without a corresponding result */
    for (ptr = list; ptr && NotRet(ptr); ptr = ptr->next) {         /* all queries */
        if ((fields[ptr->field].attrib & ATTR_INDEXED) &&  /* this is indexed */
            (ptr->field != atoi(NAME_FIELD)) &&
            (ptr->field != atoi(NICKNAME_FIELD))) {   /* that isn't name= */
            for (ind = end - matched; ind < end; ind++)
                if (results[ind].field == ptr->field)
                    break;       /* a result matched this query */
            if (ind == end)      /* didn't find a match */
                return False;    /* for this query */
        }
    }
    return True;
}


/* see if a query field matches the id */
int lookup(int sock, int id, Arg *arg)
{
    int status;
    char *cp, value[DATA_SIZE + 1], data[DATA_SIZE + 1];
    $DESCRIPTOR(value_dsc, value);
    $DESCRIPTOR(data_dsc, data);

    if (DEBUG)
        swrite(sock, "lookup %d\r\n", id);

    /* first, read the data record */
    datrab.rab$b_rac = RAB$C_KEY;
    datrab.rab$b_ksz = ID_SIZE + FIELD_SIZE;  /* partial key size */
    datrab.rab$l_rop = RAB$M_KGE;             /* find any sequence number */
    sprintf(dat_key, "%0*d%0*d%*s", ID_SIZE, id,
            FIELD_SIZE, arg->field, SEQ_SIZE, "");
    while ((status = sys$get(&datrab)) & 1) {
        datrab.rab$b_rac = RAB$C_SEQ;
        if (strncmp(dat_key, dat_record, ID_SIZE + FIELD_SIZE))
            break;                            /* finished with this id/field */  
        dat_record[datrab.rab$w_rsz] = '\0';  /* terminate string */   
        strcpy(data, dat_record + ID_SIZE + FIELD_SIZE + SEQ_SIZE + ATTR_SIZE);
        data_dsc.dsc$w_length = (short) strlen(data);
        if (DEBUG)
            swrite(sock, "-100: Lookup >> %s\r\n", dat_record);
        /* force case for compare; there's no case-blind flag to match_wild */
        for (cp = data; *cp; cp++) *cp = _tolower(*cp);
        sprintf(value, "*%s*", arg->value);
        value_dsc.dsc$w_length = (short) strlen(value);
        if (str$match_wild(&data_dsc, &value_dsc) & 1)
            return True;  /* one match is good enough */
    }
    return False;
}
        

/* find the non-indexed query fields in the data file 
   and see if id will match them
 */
int find_non_indexed(int sock, int id, Arg *list)
{
    Arg *ptr;

    for (ptr = list; ptr && NotRet(ptr); ptr = ptr->next) {
        if (fields[ptr->field].attrib & ATTR_INDEXED)
            continue;                   /* already did indexed fields */
        if (!lookup(sock, id, ptr))
            return False;               /* no match, you loose */
    }
    return True;                        /* all fields matched */
}


int compar(Result *a, Result *b)
{
    if (a->id < b->id) return (-1);
    if (a->id > b->id) return (1);
    if (a->field < b->field) return (-1);
    if (a->field > b->field) return (1);
    return (0);
}


/* display the requested fields associated with id */
void display_id(int sock, int id, Arg *list, int match)
{
    Arg *ptr;
    int request[MAX_FIELD], use_defaults = True;
    int max, ind, status, num, seq, attrib;
    char data[DATA_SIZE + 1], field[20];

/* first we need to know whether there are any requested fields */

    for (ind = 0; ind < MAX_FIELD; ind++)
        request[ind] = False;
    for (ptr = list; ptr; ptr = ptr->next) {
        if (ptr->type & TYPE_RETURN)
            use_defaults = False;
        else if (strcmp(ptr->value, "all") == 0)
            for (ind = 0; ind < MAX_FIELD; ind++)
                request[ind] = True;    /* mark all as requested */
        else if (!use_defaults)
            request[ptr->field] = True;
    }        

/* if use_defaults = true, the Default fields will be used
   otherwise, request[] contains the fields requested,
*/

    /* find the max field name size */
    for (ind = 0, max = 0; ind < MAX_FIELD; ind++)
        if (fields[ind].name && (strlen(fields[ind].name) > max))
            max = strlen(fields[ind].name);

    datrab.rab$b_rac = RAB$C_KEY;
    datrab.rab$b_ksz = ID_SIZE;               /* partial key size */
    datrab.rab$l_rop = RAB$M_KGE;             /* find all records */
    sprintf(dat_key, "%0*d%0*d%*s", ID_SIZE, id,
            FIELD_SIZE, 0, SEQ_SIZE, "");
    while ((status = sys$get(&datrab)) & 1) {
        datrab.rab$b_rac = RAB$C_SEQ;
        if (strncmp(dat_key, dat_record, ID_SIZE))
            break;                            /* finished with this id */  
        dat_record[datrab.rab$w_rsz] = '\0';  /* terminate string */   
        strncpy(field, dat_record + ID_SIZE, FIELD_SIZE);
        field[FIELD_SIZE] = '\0';
        num = atoi(field);
        strncpy(field, dat_record + ID_SIZE + FIELD_SIZE, SEQ_SIZE);
        field[SEQ_SIZE] = '\0';
        seq = atoi(field);
        strncpy(field, dat_record + ID_SIZE + FIELD_SIZE + SEQ_SIZE, ATTR_SIZE);
        field[ATTR_SIZE] = '\0';
        attrib = atoi(field);
        strcpy(data, dat_record + ID_SIZE + FIELD_SIZE + SEQ_SIZE + ATTR_SIZE);

        /* if a public field is requested OR no fields were requested and the
           field is a default AND the field isn't suppressed OR the field may
           not be suppressed (ForcePub) then print the record
        */
        if (((request[num] && (fields[num].attrib & ATTR_PUBLIC)) ||
             (use_defaults && (fields[num].attrib & ATTR_DEFAULT))) &&  /* Default implies Public */
            (((attrib & ATTR_SUPPRESS) == 0) || (fields[num].attrib & ATTR_FORCEPUB))) {
            swrite(sock, "-200:%d:%*s: %s\r\n", match, max,
                seq ? "" : fields[num].name, data);
        }
    }
}



/*
   given an array of indexed fields that match indexed part of query,
   return the count of actual matches with the list in results[]
*/
int resolve(int sock, Result *results, int size, Arg *list)
{
    Arg *ptr;
    int ind, iq, matches, count = 0;

    qsort((char *) results, size, sizeof(Result), compar); /* sort the hits */
    for (ptr = list, iq = 0; ptr && NotRet(ptr); ptr = ptr->next) {
        if (fields[ptr->field].attrib & ATTR_INDEXED)
            iq++;  /* Get count of indexed queries (iq) */
    }

    if (DEBUG)
        for (ind = 0; ind < size; ind++)
            swrite(sock, "-100: Resolve >> field %d  id %d\r\n",
                    results[ind].field, results[ind].id);

    /* find sequences of 'iq' matches of one id */
    for (ind = 1, matches = 1; ind < (size + 1); ind++) {
        if ((ind < size) && (results[ind-1].id == results[ind].id))
            matches++;
        else
            if ( (matches >= iq) &&          /* if everything matches */
                  validate_match(results, ind, matches, list) &&
                  find_non_indexed(sock, results[ind-1].id, list) ) {
                results[count++] = results[ind-1];  /* save the id */
                matches = 1;
            }
    }
    return count;
}


/*
   return number of indexed fields
   ensure that and all fields exist and have lookup attribute
*/
int validate_fields(int sock, Arg *list)
{
    int highest = 0, indexed = 0;
    Arg *ptr, *new;
    char *cp;

    for (ptr = list; ptr && NotRet(ptr); ptr = ptr->next) {
        if (ptr->name && (ptr->field == -1)) {
            writestring(sock, "507:Field does not exist.\r\n");
            return (0);  /* force failure */
        }
        if (ptr->field == -1) {          /* null field name is 'name' field */
            ptr->field = atoi(NAME_FIELD);
            ptr->name = new_string(fields[ptr->field].name);
        }
#if NAME_HACK
        /* mirror the code in qi_build.c */
        if (ptr->field == atoi(NAME_FIELD)) {
            cp = strchr(ptr->value, '\'');
            if (cp) strcpy(cp, cp+1);  /* squeeze out apostrophe */
            cp = strchr(ptr->value, '-');
            if (cp) {
                *cp = '\0';
                new = make_arg(ptr->name, ptr->field, cp + 1, ptr->type); /* copy second name */
                new->next = ptr->next;
                ptr->next = new;
            }
        }
#endif
        if (fields[ptr->field].attrib & ATTR_INDEXED)
            indexed++;
        if ((fields[ptr->field].attrib & ATTR_LOOKUP) == 0) {
            writestring(sock, "516:No authorization for request.\r\n");
            return (0);  /* force failure */
        }
    }
    return indexed;
}


int query(char *cmd, int sock)
{
    int status, ind, hits, size = 0;
    Arg *list, *listp, *nick;
    Result *results = NULL;

    list = parse_cmd(cmd, sock);       /* build query struct from cmd */

    if (validate_fields(sock, list) == 0)
        writestring(sock, "515:No indexed field in query.\r\n");
    else {
        for (listp = list; listp && NotRet(listp); listp = listp->next) {
            size = find(sock, listp, &results, size);    /* record ids that match query */
            /* if this is a name field, lookup nickname.  Problems with resolver? */
            if (listp->field == atoi(NAME_FIELD)) {
                nick = make_arg(listp->name, atoi(NICKNAME_FIELD), listp->value, listp->type),
                size = find(sock, nick, &results, size);
                free_args(nick);
            }
        }
    }
    if (size == 0)  /* no index matches */
        writestring(sock, "501:No matches to query.\r\n");
    else {
        hits = resolve(sock, results, size, list);
        if (hits == 0)
            writestring(sock, "501:No matches to query.\r\n");
        else if (hits > MAX_RECORDS) {
            writestring(sock, "502:Too many matches to query.\r\n");
            qilog(sock, "Too many matches (%d)", hits);
        }
        else {
            for (ind = 0; ind < hits; ind++)
                display_id(sock, results[ind].id, list, ind+1);
            qilog(sock, "Returned %d out of %d", hits, size);
            writestring(sock, "200:Ok.\r\n");
        }
    }

    free_args(list);
    free(results);
    return True;
}


void db_open()
{
    idxfab = cc$rms_fab;
    idxfab.fab$b_fac = FAB$M_GET;
    idxfab.fab$l_fna = INDEX_NAME;
    idxfab.fab$b_fns = strlen(INDEX_NAME);
    idxfab.fab$b_shr = FAB$M_SHRGET;
    idxfab.fab$v_lnm_mode = PSL$C_EXEC;
    idxfab.fab$l_xab = &idxxab;

    idxrab = cc$rms_rab;
    idxrab.rab$l_fab = &idxfab;
    idxrab.rab$l_kbf = idx_key;
    idxrab.rab$b_ksz = IDX_KEY_SIZE;
    idxrab.rab$b_rac = RAB$C_KEY;
    idxrab.rab$w_usz = IDX_RECORD_SIZE;
    idxrab.rab$l_ubf = idx_record;

    idxxab = cc$rms_xabkey;
    idxxab.xab$w_pos0 = 0;
    idxxab.xab$b_siz0 = IDX_KEY_SIZE;

    datfab = cc$rms_fab;
    datfab.fab$b_fac = FAB$M_GET;
    datfab.fab$l_fna = DATA_NAME;
    datfab.fab$b_fns = strlen(DATA_NAME);
    datfab.fab$b_shr = FAB$M_SHRGET;
    datfab.fab$v_lnm_mode = PSL$C_EXEC;
    datfab.fab$l_xab = &datxab;

    datrab = cc$rms_rab;
    datrab.rab$l_fab = &datfab;
    datrab.rab$l_kbf = dat_key;
    datrab.rab$b_ksz = DAT_KEY_SIZE;
    datrab.rab$b_rac = RAB$C_KEY;
    datrab.rab$w_usz = DAT_RECORD_SIZE;
    datrab.rab$l_ubf = dat_record;

    datxab = cc$rms_xabkey;
    datxab.xab$w_pos0 = 0;
    datxab.xab$b_siz0 = DAT_KEY_SIZE;


    if (((db_status = sys$open(&idxfab)) & 1) != SS$_NORMAL)
        return;

    if (((db_status = sys$connect(&idxrab)) & 1) != SS$_NORMAL)
        return;

    if (idxfab.fab$b_org != FAB$C_IDX) {
        db_status = 0;
        return;
    }

    if (((db_status = sys$open(&datfab)) & 1) != SS$_NORMAL)
        return;

    if (((db_status = sys$connect(&datrab)) & 1) != SS$_NORMAL)
        return;

    if (datfab.fab$b_org != FAB$C_IDX) {
        db_status = 0;
        return;
    }
}


void db_close()
{
    sys$close(&idxfab);
    sys$close(&datfab);
}
.
Response: text/plain
Original URLgopher://bitreich.org/0/gopher2007/2007-gopher-mirror/gop...
Content-Typetext/plain; charset=utf-8