Logo Search packages:      
Sourcecode: katoob version File versions  Download package

libdict.c

/* libdict.c
 * sdyoung@well.com
 *
 * This is where the higher-level functions are handled.  Low-level
 * (and mostly internal) functions are in ldsock.c and
 * ldutil.c.
 *
 * The guideline is that this file is where all exported functions
 * go.  This guideline is gleefully violated with considerable frequency.
 *
 *   Copyright (c) 2001, 2002 Steven Young
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 *   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 *   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 *   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * $Header: /home/arabeyes/cvs/projects/katoob/src/libdict/libdict.c,v 1.2 2003/11/27 18:15:59 sameer Exp $
 * $Log: libdict.c,v $
 * Revision 1.2  2003/11/27 18:15:59  sameer
 * * Replaced katoob_debug(__FUNCTION__); with a macro: KATOOB_DEBUG__FUNCTION
 * * Code cleanup: katoob compiles under g++ 2.95 & 3.3.1 "still doesn't link!".
 *
 * Revision 1.1  2003/09/12 08:32:13  sameer
 * libdict-0.9: initial import
 *
 * Revision 1.18  2002/03/15 19:09:07  steve
 * One last bugfix to ld_newconn.
 *
 * Revision 1.17  2002/03/15 18:39:03  sdyoung
 * Added license to the comment header.
 *
 * Revision 1.16  2002/03/15 18:34:13  sdyoung
 * Moved some stuff around in newconn to give better error reporting.
 *
 * Revision 1.15  2002/03/15 18:17:55  sdyoung
 * Made ld_newconn always return something so we can return error
 * status of connection attempt in dictconn->ld_errno.
 *
 * Revision 1.14  2002/03/14 05:23:08  sdyoung
 * Added support for ld_manswers/danswers - we can free it ourselves now.
 *
 * Revision 1.13  2002/03/13 21:08:20  sdyoung
 * Added ld_geterrno function; also made changes to support ld_errno being
 * in dictconn structure now.
 *
 * Revision 1.12  2002/03/12 18:13:26  sdyoung
 * Change true/false to ld_true/ld_false.
 *
 * Revision 1.11  2002/03/12 17:59:16  sdyoung
 * Changed all bool's to ld_bools (to avoid C++ conflict)
 *
 * Revision 1.10  2001/11/13 00:01:58  sdyoung
 * Made all functions that deal with quoted server replies automatically
 * unquote (eg, ld_match).
 *
 * Revision 1.9  2001/11/12 21:52:41  sdyoung
 * ld_newconn now gets the database and strategy lists automatically;
 * ld_getdbs and ld_getstrats just return pointers to the lists which
 * are stored in ld_conn structures.  This means that the library can
 * now automatically take care of ld_freedbs.
 *
 * Revision 1.8  2001/11/09 06:35:45  sdyoung
 * Added some comments.
 *
 * Revision 1.7  2001/11/02 00:11:26  sdyoung
 * Added capability to specify what CLIENT greeting will be used.
 *
 * Revision 1.6  2001/11/01 20:16:22  sdyoung
 * Added support for enum ld_servcode.
 *
 * Revision 1.5  2001/10/18 21:54:17  sdyoung
 * Corrected a whack of fairly serious memory leaks.
 *
 * Revision 1.4  2001/03/08 20:28:45  sdyoung
 * Fixed a memory leak in ld_define.
 *
 * Revision 1.3  2001/03/05 16:23:00  sdyoung
 * Added a few missing ld_xfrees to ld_freeconn().
 *
 * Revision 1.2  2001/03/03 23:16:04  sdyoung
 * Added ld_getsrvreply.
 *
 * Revision 1.1  2001/03/03 10:07:02  sdyoung
 * Initial revision
 *
 */
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <stdarg.h>

#include "libdict.h"
#include "ldutil.h"
#include "ldsock.h"

/* get server response number.  returns 0 on error with ld_errno set. */
/* note: now uses the ld_servcode enumerated type.  see ldservcodes.h */
enum ld_servcode ld_getrespno(char *msg) {
      char num[4];

      if((strlen(msg) < 3) || ((!isdigit(msg[0])) || (!isdigit(msg[1])) || (!isdigit(msg[2])))) {
            return(LD_None);
      } 

      memcpy(num, msg, 3);
      num[3] = '\0';

      return((enum ld_servcode) atoi(num));
}

/* ld_checkok will take an integer response number and simplify it for
 * you: is it success (LD_True), failure (LD_False), or was it an intermittent
 * failure (Maybe). */
ld_bool ld_checkok(int respno) {
      if(respno < 300)
            return(LD_True);
      if(respno > 399)
            return(LD_False);
      return(LD_Maybe);
}

/* get a server reply; check it for okayness.  this is used by some
 * of the simpler commands that don't deal with variable-length
 * responses */
ld_bool ld_docommand(struct ld_conn *conn, ld_bool getr, char *fmt, ...) {
      ld_bool retv = LD_True;
      va_list args;
      /* sorry guys - 8k command limit */
      char buf[BUFSIZ];

      /* first, send the command */
      va_start(args, fmt);
      vsnprintf(buf, BUFSIZ, fmt, args);
      va_end(args);

      if((ld_sockprintf(conn, buf) == LD_False)) {
            retv = LD_False;
            /* getr tells us if we need to grab and do minimal processing
             * on the return string ourselves.  this can save a lot of 
             * time. */
      } else if(getr == LD_True && ld_xreadline(conn, &conn->ld_srvreply) == LD_False) {
            retv = LD_False;
      } else if(getr == LD_True) {
            retv = ld_checkok(ld_getrespno(conn->ld_srvreply));
      }

      return(retv);
}

/* send the CLIENT info to the server */
ld_bool ld_sendclient(struct ld_conn *conn, char *client) {
      if(client) 
            return(ld_docommand(conn, LD_True, "CLIENT %s\r\n", client));
      return(ld_docommand(conn, LD_True, "CLIENT %s\r\n", LD_VERSION));
}

/* get database list.  also used to retrieve strategies */
struct ld_dbs **ld_getdblist(struct ld_conn *conn, char *cmd) {
    char *tmpbuf = NULL;
    struct ld_dbs **retv = NULL;
    int dbcount = 0;
    char *p;
    ld_bool readok;

    /* execute the command (show db or show strat) */
    if(ld_docommand(conn, LD_True, "%s\r\n", cmd) == LD_False)
        return(NULL);

    while((readok = ld_xreadline(conn, &tmpbuf)) == LD_True) {
        /* while the data hasn't ended ... */
        if(!strcmp(tmpbuf, ".")) break;

        /* create an array to hold our answer's attributes.
         * or, resize it upwards for new entries */
        retv = (struct ld_dbs **)ld_xrealloc(retv, sizeof(struct ld_dbs *) * (dbcount + 2));
        /* point our array slot to attribute structure */
        retv[dbcount] = (struct ld_dbs *)ld_xmalloc(sizeof(struct ld_dbs));
        bzero(retv[dbcount], sizeof(struct ld_dbs));

        if((p = strchr(tmpbuf, ' '))) {
            *p = '\0';
            retv[dbcount]->ld_dbname = ld_xstrdup(ld_unquote(tmpbuf));
            retv[dbcount]->ld_dbdesc = ld_xstrdup(ld_unquote(p+1));
        } else {
            retv[dbcount]->ld_dbname = ld_xstrdup(ld_unquote(tmpbuf));
        }
        dbcount++;
    }

    retv[dbcount] = NULL;

    if(!readok) {
        ld_freedbs(retv);
        return(NULL);
    } else {
        if(ld_xreadline(conn, &conn->ld_srvreply) == LD_False) {
            ld_freedbs(retv);
            return(NULL);
        }
    }

    if(tmpbuf) {
        ld_xfree(tmpbuf);
        tmpbuf = NULL;
    }

    return(retv);
}

/* get a list of databases (internal) */
struct ld_dbs **ld_igetdbs(struct ld_conn *conn) {
    return(ld_getdblist(conn, "SHOW DB"));
}

/* get a list of strategies (internal) */
struct ld_dbs **ld_igetstrats(struct ld_conn *conn) {
    return(ld_getdblist(conn, "SHOW STRAT"));
}

/* connect to a dict server.  everything except the host argument is optional
 * and can be set to 0 or NULL (whichever is appropriate).
 * this will spit you back a filled out ld_conn structure, or NULL upon
 * error. */
struct ld_conn *ld_newconn(char *host, int port, int timeout, char *client, ld_bool debug) {
      struct ld_conn *srvhandle;
      struct servent *dictport;

      srvhandle = (struct ld_conn *)ld_xmalloc(sizeof(struct ld_conn));
      bzero(srvhandle, sizeof(struct ld_conn));
      
      srvhandle->ld_host = ld_xstrdup(host);
      srvhandle->ld_timeout = timeout;
      srvhandle->ld_debug = debug;

      /* try and work out the port number for ourselves if it's not passed. */
      if(!port) {
            if(!(dictport = getservbyname("dict", "tcp"))) {
                  port = LD_DEFPORT;
            } else {
                  port = dictport->s_port;
            }
      }
      
      srvhandle->ld_port = port;

      if(ld_tcpopen(srvhandle) == LD_False) 
            return(srvhandle);

      /* ld_tcpopen has filled out ld_srvfd for us */
      
      /* now we just slurp the banner and we're done. */
      if(ld_readline(srvhandle, &srvhandle->ld_srvbanner) == LD_False) {
            srvhandle->ld_errno = LDERRNO;
            close(srvhandle->ld_srvfd);
            srvhandle->ld_srvfd = 0;
            return(srvhandle);
      }

      if((ld_checkok(ld_getrespno(srvhandle->ld_srvbanner)) != LD_True) ||
            (ld_sendclient(srvhandle, client) == LD_False)) {
            srvhandle->ld_errno = LDBADPROTO;
            return(srvhandle);
      }

      /* get databases */
      if((srvhandle->ld_dbs = ld_igetdbs(srvhandle))) 
            srvhandle->ld_strats = ld_igetstrats(srvhandle);

      return(srvhandle);
}

/* free the memory structures for conn */
void ld_freeconn(struct ld_conn *conn) {
      /* this does NOT set ld_errno because it is called after
       * ld_errno has been set all over the place. */
      if(!conn) return;
      if(conn->ld_srvfd) 
            ld_closeconn(conn);
      if(conn->ld_host)
            ld_xfree(conn->ld_host);
      if(conn->ld_srvreply)
            ld_xfree(conn->ld_srvreply);
      if(conn->ld_srvbanner)
            ld_xfree(conn->ld_srvbanner);
      if(conn->ld_srchdb)
            ld_xfree(conn->ld_srchdb);
      if(conn->ld_srchstrat)
            ld_xfree(conn->ld_srchstrat);
      if(conn->ld_dbs) 
            if(*conn->ld_dbs) 
                  ld_freedbs(conn->ld_dbs);
      if(conn->ld_strats) 
            if(*conn->ld_strats)
                  ld_freedbs(conn->ld_strats);
      if(conn->ld_manswers)
            ld_freematans(conn->ld_manswers);
      if(conn->ld_danswers)
            ld_freedefans(conn->ld_danswers);
      ld_xfree(conn);
}

/* Perform the protocol-level teardown of connection conn.  We don't
 * check a lot of return values here since we don't really care. 
 * it will return true if the disconnect went smoothly and 
 * conn->ld_srvreply has the server's goodbye message in it.
 * false if things didn't go so well in which case conn->srvreply
 * is unspecified. */
ld_bool ld_closeconn(struct ld_conn *conn) {
      ld_bool retv = LD_False;
      /* tell the server we want to quit */
      if(conn && (ld_sockprintf(conn, "QUIT\r\n") == LD_True)) {
            /* the server connection is still open, so we
             * politely wait for the server to say goodbye */
            ld_xreadline(conn, &conn->ld_srvreply); 
            retv = LD_True;
            if(conn->ld_srvfd) {
                  close(conn->ld_srvfd);
                  conn->ld_srvfd = 0;
            }
      }
      return(retv);
}

/* authenticate user with the server */
ld_bool ld_auth(struct ld_conn *conn, char *user, char *pw) {
      return(ld_docommand(conn, LD_True, "AUTH %s %s\r\n", user, pw));
}

/* return server banner */
char *ld_serverinfo(struct ld_conn *conn) {
      return(conn->ld_srvbanner);
}

/* free a database list */
void ld_freedbs(struct ld_dbs **dbs) {
      int i = 0;

      if(!dbs) return;

      while(dbs[i]) {
            if(dbs[i]->ld_dbname) ld_xfree(dbs[i]->ld_dbname);
            if(dbs[i]->ld_dbdesc) ld_xfree(dbs[i]->ld_dbdesc);
            ld_xfree(dbs[i]);
            i++;
      }

      ld_xfree(dbs);
}
      
struct ld_dbs **ld_getdbs(struct ld_conn *conn) {
      return(conn->ld_dbs);
}

struct ld_dbs **ld_getstrats(struct ld_conn *conn) {
      return(conn->ld_strats);
}

/* set the strategy to use */
ld_bool ld_setstrat(struct ld_conn *conn, char *strat) {
      int x = 0;
      ld_bool stratok = LD_False;

      while(conn->ld_strats[x]) {
            if(!strcasecmp(conn->ld_strats[x]->ld_dbname, strat)) {
                  stratok = LD_True;
                  break;
            }
            x++;
      }

      if(stratok == LD_False) {
            conn->ld_errno = LDBADARG;
            return(LD_False);
      }

      if(conn->ld_srchstrat) ld_xfree(conn->ld_srchstrat);

      conn->ld_srchstrat = ld_xstrdup(strat);
      return(LD_True);
}

/* set the current db to be searched */
ld_bool ld_setdb(struct ld_conn *conn, char *db) {
      int x = 0;
      ld_bool dbok = LD_False;

      if(strcmp(db, "*")) {
            while(conn->ld_dbs[x]) {
                  if(!strcasecmp(conn->ld_dbs[x]->ld_dbname, db)) {
                        dbok = LD_True;
                        break;
                  }
                  x++;
            }
      } else {
            dbok = LD_True;
      }

      if(dbok == LD_False) {
            conn->ld_errno = LDBADARG;
            return(LD_False);
      }

      if(conn->ld_srchdb) ld_xfree(conn->ld_srchdb);
      
      conn->ld_srchdb = ld_xstrdup(db);
      return(LD_True);
}

/* define a word.  we're not as paranoid as we should be here */
struct ld_defanswer **ld_define(struct ld_conn *conn, char *word) {
      char *tmpbuf = NULL, *p, *p2;
      int anscount = 0, anssz, cmdret;
      ld_bool readok;

      if(conn->ld_danswers) {
            ld_freedefans(conn->ld_danswers);
            conn->ld_danswers = NULL;
      }

      /* send the initial command */
      if(ld_docommand(conn, LD_False, "DEFINE %s %s\r\n", 
                      conn->ld_srchdb ? conn->ld_srchdb : "*",
                              word) == LD_False) 
            return(NULL);
      
      /* get response.. */
      if(ld_xreadline(conn, &tmpbuf) == LD_False) 
            return(NULL);

      cmdret = ld_getrespno(tmpbuf);

      /* check there are some answers */
      if(cmdret != LD_DefsFollow) {
            conn->ld_errno = LDNOMATCH;
            ld_xfree(tmpbuf);
            tmpbuf = NULL;
            return(NULL);
      }

      /* read in all the defs */
      while((readok = ld_xreadline(conn, &tmpbuf)) == LD_True) {
            if(ld_getrespno(tmpbuf) == LD_OK)
                  break;
            anssz = 0;
            conn->ld_danswers = (struct ld_defanswer **)ld_xrealloc(conn->ld_danswers, sizeof(struct ld_defanswer *) * (anscount + 2));
            conn->ld_danswers[anscount] = (struct ld_defanswer *)ld_xmalloc(sizeof(struct ld_defanswer));
            bzero(conn->ld_danswers[anscount], sizeof(struct ld_defanswer));

            if(ld_getrespno(tmpbuf) != LD_WordDBName) {
                  readok = LD_False;
                  conn->ld_errno = LDBADPROTO;
                  break;
            }

            /* ok, let's deal with the 151 line */
            if(!(p=strchr(tmpbuf + 4, ' '))) {
                  readok = LD_False;
                  conn->ld_errno = LDBADPROTO;
                  break;
            }

            if(!(p2=strchr(p + 1, ' '))) {
                  readok = LD_False;
                  conn->ld_errno = LDBADPROTO;
                  break;
            }

            *p2 = '\0';

            conn->ld_danswers[anscount]->ld_ansdict = ld_xstrdup(ld_unquote(p + 1));

            *p = '\0';

            conn->ld_danswers[anscount]->ld_answord = ld_xstrdup(ld_unquote(tmpbuf + 4));

            while((readok = ld_xreadline(conn, &tmpbuf)) == LD_True) {
                  if(!strcmp(tmpbuf, ".")) {
                        break;
                  }
                  if(conn->ld_danswers[anscount]->ld_ansdef) {
                        conn->ld_danswers[anscount]->ld_ansdef = (char *)ld_xrealloc(conn->ld_danswers[anscount]->ld_ansdef, strlen(conn->ld_danswers[anscount]->ld_ansdef) + strlen(tmpbuf) + 3);
                        strcat(conn->ld_danswers[anscount]->ld_ansdef, "\r\n");
                        strcat(conn->ld_danswers[anscount]->ld_ansdef, tmpbuf);
                  } else {
                        conn->ld_danswers[anscount]->ld_ansdef = ld_xstrdup(tmpbuf);
                  }
            }

            if(readok == LD_False)
                  break;

            anscount++;
      }

      if(readok == LD_False) {
            if(conn->ld_danswers[anscount]) {
                  if(conn->ld_danswers[anscount]->ld_ansdict) ld_xfree(conn->ld_danswers[anscount]->ld_ansdict);
                  if(conn->ld_danswers[anscount]->ld_answord) ld_xfree(conn->ld_danswers[anscount]->ld_answord);
                  if(conn->ld_danswers[anscount]->ld_ansdef) ld_xfree(conn->ld_danswers[anscount]->ld_ansdef);
                  ld_xfree(conn->ld_danswers[anscount]);
                  conn->ld_danswers[anscount] = NULL;
            }
            ld_freedefans(conn->ld_danswers);
            conn->ld_danswers = NULL;
      }

      if(readok) {
            conn->ld_danswers[anscount] = NULL;
      }

      if(tmpbuf) {
            ld_xfree(tmpbuf);
            tmpbuf = NULL;
      }

      return(conn->ld_danswers);
}

/* free an answer */
void ld_freedefans(struct ld_defanswer **answers) {
      int x = 0;

      if(!answers) return;

      while(answers[x]) {
            if(answers[x]->ld_ansdict) ld_xfree(answers[x]->ld_ansdict);
            if(answers[x]->ld_answord) ld_xfree(answers[x]->ld_answord);
            if(answers[x]->ld_ansdef) ld_xfree(answers[x]->ld_ansdef);
            ld_xfree(answers[x]);
            x++;
      }

      ld_xfree(answers);
}

/* free the answer to a match */
void ld_freematans(struct ld_matchanswer **answers) {
      int x=0;

      if(!answers) return;

      while(answers[x]) {
            if(answers[x]->ld_ansdict) ld_xfree(answers[x]->ld_ansdict);
            if(answers[x]->ld_answord) ld_xfree(answers[x]->ld_answord);
            ld_xfree(answers[x]);
            x++;
      }

      ld_xfree(answers);
}

/* this kind of shames me .. watch out for some casting */
struct ld_matchanswer **ld_match(struct ld_conn *conn, char *word) {
      char tmpbuf[BUFSIZ];

      if(conn->ld_manswers) {
            ld_freematans(conn->ld_manswers);
            conn->ld_manswers = NULL;
      }
      snprintf(tmpbuf, BUFSIZ, "MATCH %s %s %s", 
                 conn->ld_srchdb ? conn->ld_srchdb : "*",
                 conn->ld_srchstrat ? conn->ld_srchstrat : "exact",
                 word);
      return(conn->ld_manswers = (struct ld_matchanswer **)ld_getdblist(conn, tmpbuf));
}

/* this will return the server's response to the last command */
char *ld_getsrvreply(struct ld_conn *conn) {
      return(conn->ld_srvreply);
}

/* return error value from a connection */
int ld_geterrno(struct ld_conn *conn) {
      return(conn->ld_errno);
}

Generated by  Doxygen 1.6.0   Back to index