/************************************************************************************************* * Server to search an index * Copyright (C) 2003-2005 Mikio Hirabayashi * This file is part of Estraier, a personal full-text search system. * Estraier is free software; you can redistribute it and/or modify it under the terms of the GNU * General Public License as published by the Free Software Foundation; either version 2 of the * License, or any later version. * Estraier is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License along with Estraier; * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA. *************************************************************************************************/ #include "estcommon.h" #define SERVNAME "Estraier" /* name of the server program */ #define DEFINDEX "casket" /* default index file */ #define DEFCONF "estsearch.conf" /* default configuration file */ #define DEFTMPL "estsearch.tmpl" /* default template file */ #define DEFTOP "estsearch.top" /* default top page file */ #define DEFPORT 4210 /* default port number */ #define DEFDIVNUM 4 /* default number of waiting process */ #define CLLIFETIME 128 /* life time of each client */ #define SOCKBUFSIZ 2048 /* size of buffer for socket */ #define RDATAMAX 262144 /* max size of data to read */ #define SEARCHURL "/estsearch" /* URL for searching */ #define ENCODING "UTF-8" /* encoding of the search page */ #define SAVENAME "estresult.html" /* name of the file to be saved */ #define RESROOM 8 /* ratio of hits for max */ #define UNITINC 4 /* unit increasing ratio of search */ #define RELVECNUM 32 /* number of dimension of score vector */ #define URIFLEN 92 /* fixed length of a shown URI */ #define EMCLASSNUM 6 /* number of classes for em elements */ typedef struct { /* type of structure of a file */ const char *name; /* file name */ int isdir; /* whether to be a directory */ char *type; /* media type */ int mtime; /* modified time */ int size; /* size */ } MYFILE; typedef struct { /* type of structure for a cluster */ CBLIST *ids; /* list for ID numbers */ CBMAP *scores; /* map for scores */ int vector[RELVECNUM]; /* vector for calculating similarity */ int point; /* total point */ } CLUSTER; typedef struct { /* type of structure to sort scores */ const char *kbuf; /* pointer to the string */ int ksiz; /* size of the string */ int point; /* total point */ } SCWORD; enum { /* enumeration for the method */ METERR, /* error or unsupported */ METGET, /* GET */ METPOST, /* POST */ METHEAD /* HEAD */ }; /* global variables for configurations */ const char *progname = NULL; /* program name */ int isparent = FALSE; /* whether parent process */ int cllifetime = 0; /* life time of the client */ const char *host = NULL; /* host name of the server */ const char *addr = NULL; /* address of the server */ int port = 0; /* port number of the server */ const char *deftype = NULL; /* default media type */ CBMAP *authset = NULL; /* set for authentication strings */ int svsock = -1; /* file descriptor of listen socket */ int clsock = -1; /* file descriptor of talking socket */ const char *claddr = NULL; /* address of the client */ int clport = 0; /* port number of the client */ const char *clqstr = NULL; /* query string of the client */ int clmtype = METERR; /* type of the method of the client */ const CBLIST *tmpllist = NULL; /* list of template elements */ const char *toptext = NULL; /* text of the top page */ const char *prefix = NULL; /* prefix of the URI of each document */ const char *suffix = NULL; /* suffix of the URI of each document */ const CBLIST *repllist = NULL; /* list of expressions for URI replacement */ const char *diridx = NULL; /* name of the index file of a directory */ int decuri = FALSE; /* whether the URI of each document is decoded */ int boolunit = 0; /* unit number of boolean search */ int relkeys = 0; /* number of words used with relational search */ int defmax = 0; /* number of shown documents by default */ int reevmax = 0; /* max number of candidates of a regular expression */ int showkeys = 0; /* number of shown keywords of each document */ int sumall = 0; /* number of all words in summary */ int sumtop = 0; /* number of words at the top of summary */ int sumwidth = 0; /* number of words near each keyword in summary */ int clustunit = 0; /* unit number of document clustering */ int clustkeys = 0; /* number of words per shown cluster */ const char *logfile = NULL; /* path of the log file for input search conditions */ const char *ldocs = NULL; /* label shared among all documents */ const char *lperpage = NULL; /* label of the select box for showing number */ const char *lclshow = NULL; /* label of the select box for document clustering */ const char *ldrep = NULL; /* label of the select box for directory rep level */ const char *lsortsc = NULL; /* label of the option of sorting by score */ const char *lsortdt = NULL; /* label of the option of sorting by date */ const char *lreverse = NULL; /* label of the option of reverse sorting */ const char *lexasis = NULL; /* label of the option of as-is expressions */ const char *lexwild = NULL; /* label of the option of expressions with wild cards */ const char *lexregex = NULL; /* label of the option of regular expressions */ const char *lsubmit = NULL; /* label of the submit button to search */ const char *lresult = NULL; /* label of the result */ const char *lclusters = NULL; /* label of the document clusters */ const char *lhits = NULL; /* label of suffix of the hit number */ const char *lmore = NULL; /* label of the link to search thoroughly */ const char *lrelto = NULL; /* label of prefix of the seed document */ const char *ldetail = NULL; /* label of a link to show detail */ const char *lrelated = NULL; /* label of a link to search related documents */ const char *lprev = NULL; /* label of the link to go to the previous page */ const char *lnext = NULL; /* label of the link to go to the next page */ const char *lidnum = NULL; /* label for the attribute of ID number */ const char *luri = NULL; /* label for the attribute of URI */ const char *ltitle = NULL; /* label for the attribute of title */ const char *lauthor = NULL; /* label for the attribute of author */ const char *lrcpt = NULL; /* label for the attribute of recipient */ const char *lmcast = NULL; /* label for the attribute of multicast */ const char *ldate = NULL; /* label for the attribute of date */ const char *ltype = NULL; /* label for the attribute of type */ const char *lenc = NULL; /* label for the attribute of encoding */ const char *lsize = NULL; /* label for the attribute of size */ const char *lkwords = NULL; /* label for the keywords */ const char *ltext = NULL; /* label for the text */ const char *enohit = NULL; /* error message when no document hits */ const char *enoscore = NULL; /* error message when the document has no score */ const char *enoword = NULL; /* error message when no effective word */ const char *altfunc = NULL; /* name of the scripting function for each document */ /* global variables for parameters */ const char *phrase = NULL; /* search phrase */ int max = 0; /* number of shown documents */ int clshow = 0; /* number of shown clusters */ int clcode = 0; /* code of the cluster for narrowing */ int clall = FALSE; /* whether to show all clusters */ int drep = 0; /* level of directory rep */ const char *sort = NULL; /* sorting order */ const char *expr = NULL; /* expression class */ int page = 0; /* numerical order of the page */ int skip = 0; /* number of elements to be skipped */ int unit = 0; /* unit number of current search */ int relid = 0; /* ID number of the seed document */ int detid = 0; /* ID number of the shown document in detail */ int tfidf = FALSE; /* whether search scores are tuned by TF-IDF */ const char *mrglbl = NULL; /* label assigned by mergers */ int showsc = FALSE; /* whether to show score expression */ const char *relsc = NULL; /* score map of seed document */ /* other global variables */ double ustime = 0.0; /* start time of user processing */ double sstime = 0.0; /* start time of system processing */ ODEUM *odeum = NULL; /* handle of the index */ CURIA *scdb = NULL; /* handle of the score database */ DEPOT *dtdb = NULL; /* handle of the date database */ int tabidx = 0; /* counter of tab indexes */ /* function prototypes */ int main(int argc, char **argv); void usage(void); void die(const char *msg); void readconf(const char *index, const char *conffile, const char *tmplfile, const char *topfile); const char *skiplabel(const char *str); const char *mygethostaddr(const char *host); const char *mygethostname(void); int mygettcpserv(const char *addr, unsigned short port); void sigtermhandler(int num); void divideprocess(int num, int uid); void secondstage(void); void thirdstage(void); char *sockgetline(int fd); int sockgetc(int fd); void showerror(int code, const char *msg, const char *extheads); int mywrite(int fd, const void *buf, int size); const char *urltofile(const char *url); const char *datestr(int t, int local); const char *gettype(const char *path); void xmlprintf(const char *format, ...); void uriprint(const char *path, int mlen); void senddirview(const char *url, const char *query, int ims); int comparebyname(const void *a, const void *b); int comparebydate(const void *a, const void *b); int comparebytype(const void *a, const void *b); int comparebysize(const void *a, const void *b); void sendasis(const char *url, int ims); void searchmain(const char *query); CBMAP *getparams(const char *query); void showform(void); void showrelated(void); void setovec(CBMAP *scores, int *vec); void settvec(CBMAP *osc, CBMAP *tsc, int *vec); void showclusters(ODPAIR *pairs, int *np); void sumupclusters(CLUSTER *clusts, int *np); int scwordcompare(const void *a, const void *b); int clustercompare(const void *a, const void *b); void showdetail(void); void showboolean(void); char *cuturi(const char *uri, int level); void showdoc(const ODDOC *doc, int show, int score, double rel, const ESTWORD *words, int wnum); void showsummary(const ODDOC *doc, const ESTWORD *words, int wnum, int detail); void showpaging(int next, int all); void showinfo(void); void showproctime(void); void showversion(void); void outputlog(void); /* main routine */ int main(int argc, char **argv){ char *index, *conffile, *tmplfile, *topfile, *tmp; int i, div, uid; estputenv("LANG", ESTLOCALE); estputenv("LC_ALL", ESTLOCALE); cbstdiobin(); progname = argv[0]; index = NULL; conffile = NULL; tmplfile = NULL; topfile = NULL; host = NULL; port = -1; authset = cbmapopen(); cbglobalgc(authset, (void (*)(void *))cbmapclose); div = DEFDIVNUM; uid = -1; isparent = TRUE; for(i = 1; i < argc; i++){ if(!index && argv[i][0] == '-'){ if(!strcmp(argv[i], "-host")){ if(++i >= argc) usage(); host = argv[i]; } else if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = atoi(argv[i]); } else if(!strcmp(argv[i], "-div")){ if(++i >= argc) usage(); div = atoi(argv[i]); } else if(!strcmp(argv[i], "-uid")){ if(++i >= argc) usage(); uid = atoi(argv[i]); } else if(!strcmp(argv[i], "-dtype")){ if(++i >= argc) usage(); deftype = argv[i]; } else if(!strcmp(argv[i], "-auth")){ if(++i >= argc || !strchr(argv[i], ':')) usage(); tmp = cbbaseencode(argv[i], -1); cbmapput(authset, tmp, -1, "", 0, FALSE); free(tmp); } else { usage(); } } else if(!index){ index = argv[i]; } else if(!conffile){ conffile = argv[i]; } else if(!tmplfile){ tmplfile = argv[i]; } else if(!topfile){ topfile = argv[i]; } else { usage(); } } if(!index){ index = DEFINDEX; conffile = DEFCONF; tmplfile = DEFTMPL; topfile = DEFTOP; } if(!conffile || !tmplfile || !topfile) usage(); if(port < 1) port = DEFPORT; if(uid > 0 && getuid() != 0) die("only the super user can set the uid"); if(!deftype) deftype = "application/octet-stream"; readconf(index, conffile, tmplfile, topfile); if(host){ if(!(addr = mygethostaddr(host))) die("the host is unknown"); } else { host = mygethostname(); addr = "0.0.0.0"; } if((svsock = mygettcpserv(addr, port)) == -1) die("cannot open the port"); printf("%s: started: host=%s addr=%s port=%d pid=%d\n", progname, host, addr, port, (int)getpid()); fflush(stdout); signal(SIGHUP, sigtermhandler); signal(SIGINT, sigtermhandler); signal(SIGQUIT, sigtermhandler); signal(SIGTERM, sigtermhandler); signal(SIGPIPE, SIG_IGN); divideprocess(div, uid); secondstage(); return 0; } /* print the usage and exit */ void usage(void){ fprintf(stderr, "%s: indexer of document files\n", progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s [-host name] [-port num] [-div num] [-uid num] [-dtype type]" " [-auth user:pass] [index conffile tmplfile topfile]\n", progname); fprintf(stderr, "\n"); exit(1); } /* show an error message and exit */ void die(const char *msg){ fprintf(stderr, "%s: %s\n", progname, msg); exit(1); } /* read the configuration file */ void readconf(const char *index, const char *conffile, const char *tmplfile, const char *topfile){ CBLIST *lines, *rlt, *tlist; const char *tmp; char *tstr, path[ESTPATHBUFSIZ]; int i; /* read the main configuration file */ if(!(lines = cbreadlines(conffile))) die("the configuration file is missing."); cbglobalgc(lines, (void (*)(void *))cblistclose); rlt = cblistopen(); cbglobalgc(rlt, (void (*)(void *))cblistclose); repllist = rlt; for(i = 0; i < cblistnum(lines); i++){ tmp = cblistval(lines, i, NULL); if(cbstrfwimatch(tmp, "prefix:")){ prefix = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "suffix:")){ suffix = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "replace:")){ cblistpush(rlt, skiplabel(tmp), -1); } else if(cbstrfwimatch(tmp, "diridx:")){ diridx = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "decuri:")){ if(!cbstricmp(skiplabel(tmp), "true")) decuri = TRUE; } else if(cbstrfwimatch(tmp, "boolunit:")){ boolunit = atoi(skiplabel(tmp)); } else if(cbstrfwimatch(tmp, "relkeys:")){ relkeys = atoi(skiplabel(tmp)); } else if(cbstrfwimatch(tmp, "defmax:")){ defmax = atoi(skiplabel(tmp)); } else if(cbstrfwimatch(tmp, "reevmax:")){ reevmax = atoi(skiplabel(tmp)); } else if(cbstrfwimatch(tmp, "showkeys:")){ showkeys = atoi(skiplabel(tmp)); } else if(cbstrfwimatch(tmp, "sumall:")){ sumall = atoi(skiplabel(tmp)); } else if(cbstrfwimatch(tmp, "sumtop:")){ sumtop = atoi(skiplabel(tmp)); } else if(cbstrfwimatch(tmp, "sumwidth:")){ sumwidth = atoi(skiplabel(tmp)); } else if(cbstrfwimatch(tmp, "clustunit:")){ clustunit = atoi(skiplabel(tmp)); } else if(cbstrfwimatch(tmp, "clustkeys:")){ clustkeys = atoi(skiplabel(tmp)); } else if(cbstrfwimatch(tmp, "logfile:")){ logfile = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "ldocs:")){ ldocs = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lperpage:")){ lperpage = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lclshow:")){ lclshow = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "ldrep:")){ ldrep = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lsortsc:")){ lsortsc = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lsortdt:")){ lsortdt = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lreverse:")){ lreverse = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lexasis:")){ lexasis = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lexwild:")){ lexwild = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lexregex:")){ lexregex = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lsubmit:")){ lsubmit = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lresult:")){ lresult = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lclusters:")){ lclusters = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lhits:")){ lhits = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lmore:")){ lmore = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lrelto:")){ lrelto = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "ldetail:")){ ldetail = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lrelated:")){ lrelated = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lprev:")){ lprev = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lnext:")){ lnext = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lidnum:")){ lidnum = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "luri:")){ luri = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "ltitle:")){ ltitle = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lauthor:")){ lauthor = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lrcpt:")){ lrcpt = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lmcast:")){ lmcast = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "ldate:")){ ldate = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "ltype:")){ ltype = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lsize:")){ lsize = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lkwords:")){ lkwords = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "ltext:")){ ltext = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "lenc:")){ lenc = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "enohit:")){ enohit = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "enoscore:")){ enoscore = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "enoword:")){ enoword = skiplabel(tmp); } else if(cbstrfwimatch(tmp, "altfunc:")){ altfunc = skiplabel(tmp); } } if(!prefix) prefix = ""; if(!suffix) suffix = ""; if(!diridx) diridx = ""; if(boolunit < 1) die("boolunit is undefined."); if(relkeys < 1) die("relkeys is undefined."); if(defmax < 1) die("defmax is undefined."); if(reevmax < 1) die("reevmax is undefined."); if(showkeys < 1) die("showkeys is undefined."); if(sumall < 1) die("sumall is undefined."); if(sumtop < 1) die("sumtop is undefined."); if(sumwidth < 1) die("sumwidth is undefined."); if(!logfile) logfile = ""; if(!ldocs) ldocs = ""; if(!lperpage) die("lperpage is undefined."); if(!lclshow) die("lclshow is undefined."); if(!ldrep) die("ldrep is undefined."); if(!lsortsc) die("lsortsc is undefined."); if(!lsortdt) die("lsortdt is undefined."); if(!lreverse) die("lreverse is undefined."); if(!lexasis) die("lexasis is undefined."); if(!lexwild) die("lexwild is undefined."); if(!lexregex) die("lexregex is undefined."); if(!lsubmit) die("lsubmit is undefined."); if(!lresult) die("lresult is undefined."); if(!lclusters) die("lclusters is undefined."); if(!lhits) die("lhits is undefined."); if(!lmore) die("lmore is undefined."); if(!lrelto) die("lrelto is undefined."); if(!ldetail) die("ldetail is undefined."); if(!lrelated) die("lrelated is undefined."); if(!lprev) die("lprev is undefined."); if(!lnext) die("lnext is undefined."); if(!lidnum) die("lidnum is undefined."); if(!luri) die("luri is undefined."); if(!ltitle) die("ltitle is undefined."); if(!lauthor) die("lauthor is undefined."); if(!lrcpt) die("lrcpt is undefined."); if(!lmcast) die("lmcast is undefined."); if(!ldate) die("ldate is undefined."); if(!ltype) die("ltype is undefined."); if(!lenc) die("lenc is undefined."); if(!lsize) die("lsize is undefined."); if(!lkwords) die("lkwords is undefined."); if(!ltext) die("ltext is undefined."); if(!enohit) die("enohit is undefined."); if(!enoscore) die("enoscore is undefined."); if(!enoword) die("enoword is undefined."); if(!altfunc) altfunc = ""; /* read the template file */ if(!(tstr = cbreadfile(tmplfile, NULL))) die("the template file is missing."); tlist = cbxmlbreak(tstr, FALSE); cbglobalgc(tlist, (void (*)(void *))cblistclose); free(tstr); tmpllist = tlist; /* read the top page file */ if(!(tstr = cbreadfile(topfile, NULL))) die("the template file is missing."); cbglobalgc(tstr, free); toptext = tstr; /* open the databases */ if(!(odeum = odopen(index, OD_OREADER))) die("the index is missing or broken."); cbglobalgc(odeum, (void (*)(void *))odclose); sprintf(path, "%s%c%s", index, ESTPATHCHR, ESTSCDBNAME); if((scdb = cropen(path, CR_OREADER, -1, -1)) != NULL){ cbglobalgc(scdb, (void (*)(void *))dpclose); crbusenum(scdb); } sprintf(path, "%s%c%s", index, ESTPATHCHR, ESTDTDBNAME); if((dtdb = dpopen(path, DP_OREADER, -1)) != NULL){ cbglobalgc(dtdb, (void (*)(void *))dpclose); dpbusenum(dtdb); } printf("%s: database opened: fsiz=%.0f dnum=%d wnum=%d bnum=%d busenum=%d\n", progname, odfsiz(odeum), oddnum(odeum), odwnum(odeum), odbnum(odeum), odbusenum(odeum)); } /* skip the label of a line */ const char *skiplabel(const char *str){ if(!(str = strchr(str, ':'))) return str; str++; while(*str != '\0' && (*str == ' ' || *str == '\t')){ str++; } return str; } /* get the address of a hostname */ const char *mygethostaddr(const char *host){ struct hostent *info; char *buf1; static char buf2[ESTPATHBUFSIZ]; if(!(info = gethostbyname(host)) || !info->h_addr_list[0]) return NULL; buf1 = inet_ntoa(*(struct in_addr *)info->h_addr_list[0]); sprintf(buf2, "%s", buf1); return buf2; } /* get the hostname */ const char *mygethostname(void){ static char myhost[ESTPATHBUFSIZ]; if(gethostname(myhost, ESTPATHBUFSIZ - 1) == -1) return "(unknown)"; return myhost; } /* get the server socket of an address and a port */ int mygettcpserv(const char *addr, unsigned short port){ int sockfd, optone; struct sockaddr_in address; struct linger li; memset(&address, 0, sizeof(address)); address.sin_family = AF_INET; if(!inet_aton(addr, &address.sin_addr)) return -1; address.sin_port = htons(port); if((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) return -1; li.l_onoff = 1; li.l_linger = 100; optone = 1; if(setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *)&li, sizeof(li)) == -1 || setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&optone, sizeof(optone)) == -1){ close(sockfd); return -1; } if(bind(sockfd, (struct sockaddr *)&address, sizeof(address)) == -1 || listen(sockfd, SOMAXCONN) == -1){ close(sockfd); return -1; } return sockfd; } /* handler for termination signals */ void sigtermhandler(int num){ int pid; pid = getpid(); printf("%s: a signal catched: sig=%d pid=%d\n", progname, num, pid); fflush(stdout); if(isparent){ printf("%s: waiting children\n", progname); fflush(stdout); signal(SIGTERM, SIG_IGN); kill(-pid, SIGTERM); while(TRUE){ if(waitpid(-pid, NULL, 0) < 1) break; } printf("%s: finished: pid=%d\n", progname, pid); fflush(stdout); } exit(0); } /* divide the process */ void divideprocess(int num, int uid){ int i, pid, cpid; pid = getpid(); cpid = 0; if(num < 1){ printf("%s: running in single mode\n", progname); fflush(stdout); return; } for(i = 0; i < num || (cpid = waitpid(-pid, NULL, 0)) > 0; i++){ if(cpid > 0){ printf("%s: a child process died: pid=%d\n", progname, cpid); fflush(stdout); } cpid = fork(); switch(cpid){ case -1: printf("%s: cannot fork\n", progname); fflush(stdout); break; case 0: isparent = FALSE; cllifetime = CLLIFETIME; if(setpgid(0, pid) == -1){ printf("%s: cannot set pgid or sid\n", progname); fflush(stdout); exit(1); } if(uid > 0 && setuid(uid) == -1){ printf("%s: cannot set uid\n", progname); fflush(stdout); exit(1); } return; default: printf("%s: born a child process: pid=%d\n", progname, cpid); fflush(stdout); break; } cpid = 0; } } /* accept sessions */ void secondstage(void){ static char addrbuf[ESTNUMBUFSIZ]; struct sockaddr_in address; socklen_t socklen; socklen = sizeof(address); while((clsock = accept(svsock, (struct sockaddr *)&address, &socklen)) >= 0 || errno == EINTR){ if(clsock < 0) continue; sprintf(addrbuf, "%s", inet_ntoa(address.sin_addr)); claddr = addrbuf; clport = (int)ntohs(address.sin_port); thirdstage(); xmlprintf(NULL); shutdown(clsock, SHUT_RDWR); close(clsock); cllifetime--; if(!isparent && cllifetime < 1){ printf("%s: lifetime is over: pid=%d\n", progname, (int)getpid()); exit(0); } } } /* process a connection */ void thirdstage(void){ static char qstr[SOCKBUFSIZ]; const char *cstr; char *line, *pivot, url[SOCKBUFSIZ], query[SOCKBUFSIZ], *pquery, *wp, cpath[ESTPATHBUFSIZ]; int isdir, auth, clen, ims, rv; if(!(line = sockgetline(clsock))) return; sprintf(qstr, "%s", line); clqstr = qstr; clmtype = METERR; if(cbstrfwmatch(line, "GET ")){ line += 4; clmtype = METGET; } if(cbstrfwmatch(line, "POST ")){ line += 5; clmtype = METPOST; } if(cbstrfwmatch(line, "HEAD ")){ line += 5; clmtype = METHEAD; } while(*line == ' '){ line++; } pivot = line; while(*line != '\0' && *line != ' '){ line++; } *line = '\0'; sprintf(url, "%s", pivot); query[0] = '\0'; if((pivot = strchr(url, '?')) != NULL){ *pivot = '\0'; sprintf(query, "%s", pivot + 1); } estputenv("QUERY_STRING", query); auth = cbmaprnum(authset) < 1; clen = -1; ims = -1; while(TRUE){ if(!(line = sockgetline(clsock))) break; if(cbstrfwimatch(line, "content-length:")){ clen = atoi(skiplabel(line)); } else if(cbstrfwimatch(line, "if-modified-since:")){ ims = eststrmktime(skiplabel(line)); } else if(cbstrfwimatch(line, "authorization:")){ cstr = skiplabel(line); if(cbstrfwimatch(cstr, "basic") && (cstr = strchr(cstr, ' ')) != NULL){ while(*cstr == ' '){ cstr++; } if(cbmapget(authset, cstr, -1, NULL)) auth = TRUE; } } } if(!auth){ showerror(401, "Authorization Required", NULL); return; } if(clmtype == METERR){ showerror(501, "Not Implemented", NULL); return; } if(clmtype != METPOST && clen > 0){ showerror(400, "Bad Request", NULL); return; } if(url[0] != '/'){ showerror(403, "Forbidden", NULL); return; } pquery = NULL; if(clmtype == METPOST){ if(clen < 0){ showerror(411, "Length Required", NULL); return; } if(clen > RDATAMAX){ showerror(413, "Request Entity Too Large", NULL); return; } pquery = cbmalloc(clen + 1); wp = pquery; while((rv = read(clsock, wp, clen)) > 0){ clen -= rv; wp += rv; } *wp = '\0'; } if(!strcmp(url, SEARCHURL)){ searchmain(pquery ? pquery : query); } else if(strstr(url, "/../") || cbstrbwmatch(url, "/..")){ showerror(403, "Forbidden", NULL); } else if(cbstrbwmatch(url, "/")){ if(diridx[0] != '\0'){ sprintf(cpath, "%s%s", urltofile(url), diridx); if(cbfilestat(cpath, &isdir, NULL, NULL) && !isdir){ sprintf(cpath, "%s%s", url, diridx); sendasis(cpath, ims); } else { senddirview(url, pquery ? pquery : query, ims); } } else { senddirview(url, pquery ? pquery : query, ims); } } else { sendasis(url, ims); } free(pquery); } /* get a line from a socket */ char *sockgetline(int fd){ static char line[SOCKBUFSIZ]; int i, c; for(i = 0; i < SOCKBUFSIZ - 1 && (c = sockgetc(fd)) != -1; i++){ line[i] = c; if(c == '\n') break; } line[i] = '\0'; if(i > 0 && line[i-1] == '\r'){ line[i-1] = '\0'; i--; } if(i < 1) return NULL; return line; } /* get a character from a socket */ int sockgetc(int fd){ unsigned char c; return read(fd, &c, 1) == 1 ? c : -1; } /* send an error message */ void showerror(int code, const char *msg, const char *extheads){ char line[SOCKBUFSIZ]; sprintf(line, "HTTP/1.0 %d %s\r\n", code, msg); mywrite(clsock, line, -1); sprintf(line, "Date: %s\r\n", datestr(time(NULL), FALSE)); mywrite(clsock, line, -1); sprintf(line, "Server: %s/%s\r\n", SERVNAME, _EST_VERSION); mywrite(clsock, line, -1); if(code == 401){ sprintf(line, "WWW-Authenticate: Basic realm=\"%s\"\r\n", SERVNAME); mywrite(clsock, line, -1); } if(extheads) mywrite(clsock, extheads, -1); mywrite(clsock, "Connection: close\r\n", -1); mywrite(clsock, "Cache-Control: no-cache, must-revalidate, no-transform\r\n", -1); mywrite(clsock, "Pragma: no-cache\r\n", -1); sprintf(line, "Content-Type: text/html; charset=%s\r\n", ENCODING); mywrite(clsock, line, -1); mywrite(clsock, "\r\n", -1); if(clmtype != METHEAD && code != 304){ xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("
\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("%@
\n", msg); xmlprintf("\n"); xmlprintf("\n"); } printf("%s: %s: %d: %s: %d\n", progname, claddr, clport, clqstr, code); fflush(stdout); } /* Write into a file. */ int mywrite(int fd, const void *buf, int size){ const char *lbuf; int rv, wb; if(size < 0) size = strlen(buf); lbuf = buf; rv = 0; do { wb = write(fd, lbuf, size); switch(wb){ case -1: if(errno != EINTR) return -1; case 0: break; default: lbuf += wb; size -= wb; rv += wb; break; } } while(size > 0); return rv; } /* make the file path from a URI */ const char *urltofile(const char *url){ static char path[ESTPATHBUFSIZ+16]; char *tmp; int i; tmp = cburldecode(url, NULL); for(i = 0; tmp[i] != '\0'; i++){ if(tmp[i] == '/') tmp[i] = ESTPATHCHR; } sprintf(path, "%s%s", ESTCDIRSTR, tmp); free(tmp); return path; } /* get static string of the date in RFC822 format */ const char *datestr(int t, int local){ static char buf[64], *wp; struct tm *tp, lt, gt; time_t tt; int lag; tt = (time_t)t; if(!(tp = gmtime(&tt))) return "Thu, 01 Jan 1970 00:00:00 GMT"; gt = *tp; if(!(tp = localtime(&tt))) return "Thu, 01 Jan 1970 00:00:00 GMT"; lt = *tp; lag = (lt.tm_hour * 60 + lt.tm_min) - (gt.tm_hour * 60 + gt.tm_min); if(lt.tm_year > gt.tm_year){ lag += 24 * 60; } else if(lt.tm_year < gt.tm_year){ lag -= 24 * 60; } else if(lt.tm_mon > gt.tm_mon){ lag += 24 * 60; } else if(lt.tm_mon < gt.tm_mon){ lag -= 24 * 60; } else if(lt.tm_mday > gt.tm_mday){ lag += 24 * 60; } else if(lt.tm_mday < gt.tm_mday){ lag -= 24 * 60; } if(local){ wp = buf; wp += strftime(buf, sizeof(buf) - 1, "%a, %d %b %Y %H:%M:%S", <); sprintf(wp, " %+03d%02d", lag / 60, lag % 60); } else { strftime(buf, sizeof(buf) - 1, "%a, %d %b %Y %H:%M:%S GMT", >); } return buf; } /* get the media type of a file */ const char *gettype(const char *path){ char *types[] = { ".txt", "text/plain", ".txt.en", "text/plain", ".txt.ja", "text/plain", ".asc", "text/plain", ".in", "text/plain", ".c", "text/plain", ".h", "text/plain", ".cc", "text/plain", ".java", "text/plain", ".sh", "text/plain", ".pl", "text/plain", ".rb", "text/plain", ".csv", "text/plain", ".log", "text/plain", ".conf", "text/plain", ".rc", "text/plain", ".ini", "text/plain", ".html", "text/html", ".html.en", "text/html", ".html.ja", "text/html", ".htm", "text/html", ".css", "text/css", ".js", "text/javascript", ".tsv", "text/tab-separated-values", ".eml", "message/rfc822", ".mht", "message/rfc822", ".sgml", "application/sgml", ".sgm", "application/sgml", ".xml", "application/xml", ".xsl", "application/xml", ".xslt", "application/xslt+xml", ".xhtml", "application/xhtml+xml", ".xht", "application/xhtml+xml", ".rdf", "application/rdf+xml", ".rss", "application/rss+xml", ".dtd", "application/xml-dtd", ".rtf", "application/rtf", ".pdf", "application/pdf", ".ps", "application/postscript", ".eps", "application/postscript", ".doc", "application/msword", ".xls", "application/vnd.ms-excel", ".ppt", "application/vnd.ms-powerpoint", ".xdw", "application/vnd.fujixerox.docuworks", ".swf", "application/x-shockwave-flash", ".zip", "application/zip", ".tar", "application/x-tar", ".gz", "application/x-gzip", ".bz2", "application/octet-stream", ".z", "application/octet-stream", ".lha", "application/octet-stream", ".lzh", "application/octet-stream", ".cab", "application/octet-stream", ".rar", "application/octet-stream", ".sit", "application/octet-stream", ".bin", "application/octet-stream", ".o", "application/octet-stream", ".so", "application/octet-stream", ".exe", "application/octet-stream", ".dll", "application/octet-stream", ".class", "application/octet-stream", ".png", "image/png", ".gif", "image/gif", ".jpg", "image/jpeg", ".jpeg", "image/jpeg", ".tif", "image/tiff", ".tiff", "image/tiff", ".bmp", "image/bmp", ".au", "audio/basic", ".snd", "audio/basic", ".mid", "audio/midi", ".midi", "audio/midi", ".mp2", "audio/mpeg", ".mp3", "audio/mpeg", ".wav", "audio/x-wav", ".mpg", "video/mpeg", ".mpeg", "video/mpeg", ".qt", "video/quicktime", ".mov", "video/quicktime", ".avi", "video/x-msvideo", NULL }; int i; for(i = 0; types[i]; i += 2){ if(cbstrbwimatch(path, types[i])) return types[i+1]; } return deftype; } /* XML-oriented printf */ void xmlprintf(const char *format, ...){ static CBDATUM *datum = NULL; va_list ap; char *tmp, numbuf[ESTNUMBUFSIZ]; unsigned char c; if(!datum){ datum = cbdatumopen("", 0); cbglobalgc(datum, (void (*)(void *))cbdatumclose); } if(!format){ mywrite(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumsetsize(datum, 0); return; } va_start(ap, format); while(*format != '\0'){ if(*format == '%'){ format++; switch(*format){ case 'c': c = va_arg(ap, int); cbdatumcat(datum, (char *)&c, 1); break; case 's': tmp = va_arg(ap, char *); if(!tmp) tmp = "(null)"; cbdatumcat(datum, tmp, -1); break; case 'd': sprintf(numbuf, "%d", va_arg(ap, int)); cbdatumcat(datum, numbuf, -1); break; case '@': tmp = va_arg(ap, char *); if(!tmp) tmp = "(null)"; while(*tmp){ switch(*tmp){ case '&': cbdatumcat(datum, "&", -1); break; case '<': cbdatumcat(datum, "<", -1); break; case '>': cbdatumcat(datum, ">", -1); break; case '"': cbdatumcat(datum, """, -1); break; default: if(!((*tmp >= 0 && *tmp <= 0x8) || (*tmp >= 0x0e && *tmp <= 0x1f))) cbdatumcat(datum, tmp, 1); break; } tmp++; } break; case '?': tmp = va_arg(ap, char *); if(!tmp) tmp = "(null)"; while(*tmp){ c = *(unsigned char *)tmp; if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c != '\0' && strchr("_-.", c))){ cbdatumcat(datum, (char *)&c, 1); } else { sprintf(numbuf, "%%%02X", c); cbdatumcat(datum, numbuf, -1); } tmp++; } break; case '$': tmp = va_arg(ap, char *); uriprint(tmp, -1); break; case '#': tmp = va_arg(ap, char *); uriprint(tmp, URIFLEN); break; case '%': cbdatumcat(datum, "%", 1); break; } } else { cbdatumcat(datum, format, 1); } format++; } va_end(ap); } /* print a URI */ void uriprint(const char *path, int mlen){ CBDATUM *ubuf; char *uri, buf[ESTPATHBUFSIZ], *bef, *aft, *rp; int i, len; uri = cbsprintf("%s%s%s", prefix, path, suffix); for(i = 0; i < cblistnum(repllist); i++){ sprintf(buf, "%s", cblistval(repllist, i, NULL)); cbstrtrim(buf); if(buf[0] == '\0') continue; bef = buf; if((aft = strrchr(buf, ' ')) != NULL){ *aft = '\0'; aft++; } else { aft = ""; } cbstrtrim(bef); ubuf = cbdatumopen("", 0); rp = uri; while(*rp != '\0'){ if(cbstrfwimatch(rp, bef)){ cbdatumcat(ubuf, aft, -1); rp += strlen(bef); } else { cbdatumcat(ubuf, rp, 1); rp++; } } free(uri); uri = cbdatumtomalloc(ubuf, NULL); } if(mlen >= 0 && (len = strlen(uri)) > mlen + 5) sprintf(uri + mlen, "..."); xmlprintf("%@", uri); free(uri); } /* send the content of a file */ void senddirview(const char *url, const char *query, int ims){ const char *path, *name; char headbuf[SOCKBUFSIZ+32], cpath[ESTPATHBUFSIZ]; int i, isdir, size, mtime, fnum, stype; CBLIST *list; MYFILE *files; path = urltofile(url); if(!cbfilestat(path, &isdir, NULL, &mtime)){ if(errno == EACCES){ showerror(403, "Forbidden", NULL); } else { showerror(404, "File Not Found", NULL); } return; } if(ims > 0 && mtime <= ims){ showerror(304, "Not Modified", NULL); return; } if(!(list = cbdirlist(path))){ if(errno == EACCES){ showerror(403, "Forbidden", NULL); } else { showerror(404, "File Not Found", NULL); } return; } mywrite(clsock, "HTTP/1.0 200 OK\r\n", -1); sprintf(headbuf, "Date: %s\r\n", datestr(time(NULL), FALSE)); mywrite(clsock, headbuf, -1); sprintf(headbuf, "Server: %s/%s\r\n", SERVNAME, _EST_VERSION); mywrite(clsock, headbuf, -1); mywrite(clsock, "Connection: close\r\n", -1); sprintf(headbuf, "Last-Modified: %s\r\n", datestr(mtime, FALSE)); mywrite(clsock, headbuf, -1); mywrite(clsock, "Cache-Control: no-cache, must-revalidate, no-transform\r\n", -1); mywrite(clsock, "Pragma: no-cache\r\n", -1); sprintf(headbuf, "Content-Type: text/html; charset=%s\r\n", ENCODING); mywrite(clsock, headbuf, -1); mywrite(clsock, "\r\n", -1); if(clmtype != METHEAD){ xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n", ENCODING); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("| Name | \n"); xmlprintf("Date | \n"); xmlprintf("Type | \n"); xmlprintf("Size | \n"); xmlprintf("
|---|---|---|---|
| %@%s | \n", name, files[i].isdir ? "/" : "", name, files[i].isdir ? "/" : ""); xmlprintf("%@ | \n", datestr(files[i].mtime, TRUE)); xmlprintf("%@ | \n", files[i].type); xmlprintf("%d | \n", files[i].size); xmlprintf("
%@
\n", enohit); return; } scores = cbmapopenex(RELVECNUM * 2); lines = cbsplit(relsc, -1, "\n"); for(i = 0; i < cblistnum(lines); i++){ word = cblistval(lines, i, &wsiz); if((ep = strchr(word, '\t')) != NULL){ cbmapput(scores, word, ep - word, ep + 1, -1, FALSE); } } cblistclose(lines); } else { if(!(doc = odgetbyid(odeum, relid))){ xmlprintf("%@
\n", enohit); return; } if(!scdb || !(mbuf = crget(scdb, (char *)&relid, sizeof(int), 0, -1, &msiz))){ xmlprintf("%@
\n", enoscore); oddocclose(doc); return; } scores = cbmapload(mbuf, msiz); } /* search for the targets */ mywords = cbmapkeys(scores); myphrase = cbdatumopen("", 0); for(i = 0; i < relkeys && i < cblistnum(mywords); i++){ word = cblistval(mywords, i, &wsiz); if(i > 0) cbdatumcat(myphrase, " [OR] ", -1); cbdatumcat(myphrase, word, wsiz); } words = estsearchwords(cbdatumptr(myphrase), &wnum, FALSE); pairs = estsearch(odeum, words, wnum, unit, tfidf, &pnum, &lnum, FALSE, FALSE, 0); /* get relational order */ setovec(scores, ovec); for(i = 0; i < pnum; i++){ if((tmbuf = crget(scdb, (char *)&(pairs[i].id), sizeof(int), 0, -1, &tmsiz)) != NULL){ tsc = cbmapload(tmbuf, tmsiz); free(tmbuf); } else { tsc = cbmapopenex(1); } settvec(scores, tsc, tvec); cbmapclose(tsc); pairs[i].score = odvectorcosine(ovec, tvec, RELVECNUM) * 10000; if(pairs[i].score >= 9999) pairs[i].score = 10000; } odpairssort(pairs, pnum); for(i = 0; i < pnum; i++){ if(pairs[i].score < 1){ pnum = i; break; } } /* show header of result */ if(doc){ uri = oddocuri(doc); ruri = oddocgetattr(doc, "realuri"); if(ruri){ ubuf = cbmemdup(ruri, -1); } else { if(cbstrfwmatch(uri, "./")) uri += 2; ubuf = decuri ? cburldecode(uri, NULL) : cbmemdup(uri, -1); ulen = strlen(diridx); if(ulen > 0 && cbstrbwmatch(ubuf, diridx)){ ulen = strlen(ubuf) - ulen; if(ulen < 1 || ubuf[ulen-1] == '/') ubuf[ulen] = '\0'; } } } else { ubuf = cbmemdup("", 0); ulen = 0; } xmlprintf("%@
\n", enohit); /* show clusters */ if(scdb && clustunit > 0 && clshow > 0 && clustkeys > 0 && pnum > 0) showclusters(pairs, &pnum); /* show each document */ show = 0; step = 0; dirs = NULL; if(drep > 0) dirs = cbmapopen(); for(i = skip; i < pnum && show < max; i++){ if(!(tdoc = odgetbyid(odeum, pairs[i].id))) continue; if(dirs){ tmp = cuturi(oddocuri(tdoc), drep); if(cbmapget(dirs, tmp, -1, NULL)){ free(tmp); oddocclose(tdoc); continue; } cbmapput(dirs, tmp, -1, "", 0, FALSE); free(tmp); } if(skip < 1 && page > 1 && step < (page - 1) * max){ oddocclose(tdoc); step++; continue; } show++; showdoc(tdoc, show, -1, pairs[i].score / 100.0, NULL, 0); oddocclose(tdoc); } showpaging(i, pnum); /* release resouces */ if(dirs) cbmapclose(dirs); free(ubuf); free(pairs); estfreewords(words, wnum); cbdatumclose(myphrase); cblistclose(mywords); cbmapclose(scores); if(mbuf) free(mbuf); if(doc) oddocclose(doc); } /* set the original score vector */ void setovec(CBMAP *scores, int *vec){ const char *kbuf; int i, ksiz; cbmapiterinit(scores); for(i = 0; i < RELVECNUM; i++){ if((kbuf = cbmapiternext(scores, &ksiz)) != NULL){ vec[i] = atoi(cbmapget(scores, kbuf, ksiz, NULL)); } else { vec[i] = 0; } } } /* set the target score vector */ void settvec(CBMAP *osc, CBMAP *tsc, int *vec){ const char *kbuf, *vbuf; int i, ksiz; cbmapiterinit(osc); for(i = 0; i < RELVECNUM; i++){ if((kbuf = cbmapiternext(osc, &ksiz)) != NULL){ vbuf = cbmapget(tsc, kbuf, ksiz, NULL); vec[i] = vbuf ? atoi(vbuf) : 0; } else { vec[i] = 0; } } } /* show clusters */ void showclusters(ODPAIR *pairs, int *np){ CLUSTER *clusts; CBMAP **tails; ODPAIR *tpairs; const char *kbuf; char *mbuf, numbuf[ESTNUMBUFSIZ]; int i, j, pnum, blnum, clnum, tnum, msiz, ssum, mi, ovec[RELVECNUM], tvec[RELVECNUM]; double msc, sc; pnum = *np; if(pnum > clustunit) pnum = clustunit; blnum = *np; if(blnum > boolunit) blnum = boolunit; clusts = cbmalloc(pnum * sizeof(CLUSTER) + 1); clnum = 0; tails = cbmalloc(blnum * sizeof(CBMAP *) + 1); tpairs = cbmalloc(blnum * sizeof(ODPAIR) + 1); tnum = 0; for(i = 0; i < blnum; i++){ if(i < pnum){ if(!(mbuf = crget(scdb, (char *)&(pairs[i].id), sizeof(int), 0, -1, &msiz))) continue; clusts[clnum].ids = cblistopen(); sprintf(numbuf, "%d:%d", pairs[i].id, pairs[i].score); cblistpush(clusts[clnum].ids, numbuf, -1); clusts[clnum].scores = cbmapload(mbuf, msiz); free(mbuf); setovec(clusts[clnum].scores, clusts[clnum].vector); clusts[clnum].point = pairs[i].score; clnum++; } else { if(!(mbuf = crget(scdb, (char *)&(pairs[i].id), sizeof(int), 0, -1, &msiz))) continue; tails[tnum] = cbmapload(mbuf, msiz); free(mbuf); tpairs[tnum].id = pairs[i].id; tpairs[tnum].score = pairs[i].score; tnum++; } } sumupclusters(clusts, &clnum); for(i = 0; i < tnum; i++){ mi = -1; msc = 0.0; setovec(tails[i], ovec); for(j = 0; j < clnum; j++){ settvec(tails[i], clusts[j].scores, tvec); sc = odvectorcosine(ovec, tvec, RELVECNUM); if(mi < 0 || sc > msc){ mi = j; msc = sc; } } if(!clall && msc < 0.55) continue; if(msc < 0.25) continue; sprintf(numbuf, "%d:%d", tpairs[i].id, tpairs[i].score); cblistpush(clusts[mi].ids, numbuf, -1); } ssum = 0; for(i = 0; i < clnum && (clall || i < clshow); i++){ ssum += cblistnum(clusts[i].ids); } xmlprintf("%@
\n", enohit); estfreewords(words, wnum); return; } id = oddocid(doc); uri = oddocuri(doc); ruri = oddocgetattr(doc, "realuri"); title = oddocgetattr(doc, "title"); author = oddocgetattr(doc, "author"); rcpt = oddocgetattr(doc, "recipient"); mcast = oddocgetattr(doc, "multicast"); date = oddocgetattr(doc, "date"); type = oddocgetattr(doc, "type"); enc = oddocgetattr(doc, "encoding"); size = oddocgetattr(doc, "size"); if(ruri){ ubuf = cbmemdup(ruri, -1); } else { if(cbstrfwmatch(uri, "./")) uri += 2; ubuf = decuri ? cburldecode(uri, NULL) : cbmemdup(uri, -1); ulen = strlen(diridx); if(ulen > 0 && cbstrbwmatch(ubuf, diridx)){ ulen = strlen(ubuf) - ulen; if(ulen < 1 || ubuf[ulen-1] == '/') ubuf[ulen] = '\0'; } } /* show informations */ xmlprintf("%@
\n", enoword); estfreewords(words, wnum); return; } /* get result of search */ pairs = NULL; myunit = unit; if(drep > 0) myunit *= drep + 1; if(strcmp(sort, "score")) myunit = INT_MAX; do { if(pairs){ free(pairs); myunit *= UNITINC; } pairs = estsearch(odeum, words, wnum, myunit, tfidf, &pnum, &lnum, !strcmp(expr, "regex"), !strcmp(expr, "wild"), reevmax); } while(pnum < max * RESROOM && wnum > 1 && lnum > 0); if(!strcmp(sort, "date") || !strcmp(sort, "r-date")){ for(i = 0; i < pnum; i++){ date = 0; if(!dtdb){ if((doc = odgetbyid(odeum, pairs[i].id)) != NULL){ if((dstr = oddocgetattr(doc, "date")) != NULL) date = eststrmktime(dstr); oddocclose(doc); } } else { dpgetwb(dtdb, (char *)&(pairs[i].id), sizeof(int), 0, sizeof(int), (char *)&date); } pairs[i].score = date; } odpairssort(pairs, pnum); } if(cbstrfwmatch(sort, "r-")){ for(i = 0; i < pnum / 2; i++){ pswap = pairs[i]; pairs[i] = pairs[pnum-i-1]; pairs[pnum-i-1] = pswap; } } /* show header of result */ xmlprintf("%@
\n", enohit); /* show clusters */ if(scdb && clustunit > 0 && clshow > 0 && clustkeys > 0 && pnum > 0) showclusters(pairs, &pnum); /* show each document */ show = 0; step = 0; dirs = NULL; if(drep > 0) dirs = cbmapopen(); for(i = skip; i < pnum && show < max; i++){ if(!(doc = odgetbyid(odeum, pairs[i].id))) continue; if(dirs){ tmp = cuturi(oddocuri(doc), drep); if(cbmapget(dirs, tmp, -1, NULL)){ free(tmp); oddocclose(doc); continue; } cbmapput(dirs, tmp, -1, "", 0, FALSE); free(tmp); } if(skip < 1 && page > 1 && step < (page - 1) * max){ oddocclose(doc); step++; continue; } show++; showdoc(doc, show, pairs[i].score, -1, words, wnum); oddocclose(doc); } showpaging(i, pnum); /* release resouces */ if(dirs) cbmapclose(dirs); free(pairs); estfreewords(words, wnum); } /* cut a uri to a directory */ char *cuturi(const char *uri, int level){ char *buf, *pv; int i; buf = cbmemdup(uri, -1); for(i = 0; i < level; i++){ if(!(pv = strrchr(buf, '/'))) break; *pv = '\0'; } return buf; } /* show information of a document */ void showdoc(const ODDOC *doc, int show, int score, double rel, const ESTWORD *words, int wnum){ CBMAP *scores; CBDATUM *scbuf; const char *uri, *ruri, *title, *author, *date, *type, *enc, *size, *label, *key, *val; char *ubuf, numbuf[ESTNUMBUFSIZ], *tmp; int id, ulen, tsiz, ksiz, vsiz, top; id = oddocid(doc); uri = oddocuri(doc); ruri = oddocgetattr(doc, "realuri"); title = oddocgetattr(doc, "title"); author = oddocgetattr(doc, "author"); date = oddocgetattr(doc, "date"); type = oddocgetattr(doc, "type"); enc = oddocgetattr(doc, "encoding"); size = oddocgetattr(doc, "size"); label = ldocs[0] != '\0' ? ldocs : mrglbl; if(ruri){ ubuf = cbmemdup(ruri, -1); } else { if(cbstrfwmatch(uri, "./")) uri += 2; ubuf = decuri ? cburldecode(uri, NULL) : cbmemdup(uri, -1); ulen = strlen(diridx); if(ulen > 0 && cbstrbwmatch(ubuf, diridx)){ ulen = strlen(ubuf) - ulen; if(ulen < 1 || ubuf[ulen-1] == '/') ubuf[ulen] = '\0'; } } if(!title || strlen(title) < 1) title = "(untitled)"; xmlprintf("... ");
/* show phrases around search words */
tb = wnum > 0 && wnum < 4 ? (sumwidth / 2) / wnum : 0;
pv = i;
while(i < lnum){
if(cbmaprnum(kmap) > 0 && cbmaprnum(tmap) < 1){
cbmapclose(tmap);
tmap = cbmapdup(kmap);
}
normal = cblistval(nwords, i, &nwsiz);
asis = cblistval(awords, i, &awsiz);
if(awsiz > 0 && cbmapget(tmap, normal, nwsiz, NULL)){
cbmapout(tmap, normal, nwsiz);
bi = i - (sumwidth + tb) / 2;
bi = bi > pv ? bi : pv;
lshow = 0;
cjk = FALSE;
for(j = bi; j < lnum && j <= bi + sumwidth + tb; j++){
normal = cblistval(nwords, j, &nwsiz);
asis = cblistval(awords, j, &awsiz);
if(awsiz < 1) continue;
if(lshow > 0 && (!cjk || *(unsigned char *)asis < 0xe0)) xmlprintf(" ");
if(nwsiz > 0 && (ibuf = cbmapget(kmap, normal, nwsiz, NULL)) != NULL){
xmlprintf("%@", *(int *)ibuf % EMCLASSNUM, asis);
} else {
xmlprintf("%@", asis);
}
cjk = *(unsigned char *)asis >= 0xe0;
gshow++;
lshow++;
}
xmlprintf(" ... ");
i = j;
pv = i;
} else {
i++;
}
if(gshow > sumall) break;
}
/* infill the left space */
if(pv < lnum - sumwidth && sumall - gshow > sumwidth){
lshow = 0;
cjk = FALSE;
for(i = pv; i < lnum && gshow <= sumall; i++){
asis = cblistval(awords, i, NULL);
if(lshow > 0 && (!cjk || *(unsigned char *)asis < 0xe0)) xmlprintf(" ");
xmlprintf("%@", asis);
cjk = *(unsigned char *)asis >= 0xe0;
gshow++;
lshow++;
}
xmlprintf(" ... ");
}
/* show keywords et cetra */
id = oddocid(doc);
issc = FALSE;
if(scdb && (mbuf = crget(scdb, (char *)&id, sizeof(int), 0, -1, &msiz))){
scores = cbmapload(mbuf, msiz);
cbmapiterinit(scores);
for(i = 0; i < showkeys && (kbuf = cbmapiternext(scores, NULL)) != NULL; i++){
if(i > 0) xmlprintf(" ");
xmlprintf("");
xmlprintf("%@",
SEARCHURL, phrase, phrase[0] != '\0' ? "+" : "",
kbuf, max, drep, sort, expr, unit, clshow, kbuf);
xmlprintf("");
}
xmlprintf(" ... ");
cbmapclose(scores);
free(mbuf);
issc = TRUE;
}
if(detail){
xmlprintf("");
xmlprintf("%@", ldetail);
xmlprintf("");
} else {
xmlprintf("");
xmlprintf("%@",
SEARCHURL, phrase, max, drep, sort, expr, unit, id, clshow, ldetail);
xmlprintf("");
}
if(issc){
xmlprintf(" ");
xmlprintf("");
xmlprintf("%@",
SEARCHURL, max, drep, sort, expr, id, clshow, lrelated);
xmlprintf("");
}
/* release resources */
cbmapclose(tmap);
cbmapclose(kmap);
xmlprintf("