/************************************************************************************************* * 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("Error %d\n", code); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("

Error %d

\n", code); 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("Index of %@\n", url); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("

Index of %@

\n", url); xmlprintf("
"); if(!strcmp(url, "/")){ xmlprintf("[Parent Directory]"); } else { xmlprintf("[Parent Directory]"); } xmlprintf(" "); stype = 0; if(!strcmp(query, "sort=d")) stype = 1; if(!strcmp(query, "sort=t")) stype = 2; if(!strcmp(query, "sort=s")) stype = 3; if(stype == 0){ xmlprintf("[Sort by Name]"); } else { xmlprintf("[Sort by Name]"); } xmlprintf(" "); if(stype == 1){ xmlprintf("[Sort by Date]"); } else { xmlprintf("[Sort by Date]"); } xmlprintf(" "); if(stype == 2){ xmlprintf("[Sort by Type]"); } else { xmlprintf("[Sort by Type]"); } xmlprintf(" "); if(stype == 3){ xmlprintf("[Sort by Size]"); } else { xmlprintf("[Sort by Size]"); } xmlprintf(" "); xmlprintf("[Search]", SEARCHURL); xmlprintf("
\n"); xmlprintf("
\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); fnum = cblistnum(list); files = cbmalloc(fnum * sizeof(MYFILE)); for(i = 0; i < fnum; i++){ name = cblistval(list, i, NULL); files[i].name = name; sprintf(cpath, "%s%s", path, name); if(!cbfilestat(cpath, &isdir, &size, &mtime)){ files[i].isdir = FALSE; files[i].type = NULL; files[i].mtime = -1; files[i].size = -1; } else { if(isdir){ files[i].isdir = TRUE; files[i].type = cbmemdup("Directory", -1); } else { files[i].isdir = FALSE; files[i].type = cbmemdup(gettype(name), -1); } files[i].mtime = mtime; files[i].size = size; } } switch(stype){ case 1: cbqsort(files, fnum, sizeof(MYFILE), comparebydate); break; case 2: cbqsort(files, fnum, sizeof(MYFILE), comparebytype); break; case 3: cbqsort(files, fnum, sizeof(MYFILE), comparebysize); break; default: cbqsort(files, fnum, sizeof(MYFILE), comparebyname); break; } for(i = 0; i < fnum; i++){ name = files[i].name; if(!strcmp(name, ESTCDIRSTR) || !strcmp(name, ESTPDIRSTR)) continue; if(!(files[i].type)) continue; xmlprintf("\n"); xmlprintf("\n", name, files[i].isdir ? "/" : "", name, files[i].isdir ? "/" : ""); xmlprintf("\n", datestr(files[i].mtime, TRUE)); xmlprintf("\n", files[i].type); xmlprintf("\n", files[i].size); xmlprintf("\n"); } xmlprintf("
NameDateTypeSize
%@%s%@%@%d
\n"); xmlprintf("
\n"); xmlprintf("
%@/%@ Server at %@ Port %d
\n", SERVNAME, _EST_VERSION, host, port); xmlprintf("\n"); xmlprintf("\n"); for(i = 0; i < fnum; i++){ free(files[i].type); } free(files); } cblistclose(list); printf("%s: %s: %d: %s: 200\n", progname, claddr, clport, clqstr); fflush(stdout); } /* file comparing function by name */ int comparebyname(const void *a, const void *b){ MYFILE *ap, *bp; int rv; ap = (MYFILE *)a; bp = (MYFILE *)b; if(!ap->type && !ap->type) return 0; if(!ap->type && bp->type) return 1; if(ap->type && !bp->type) return -1; if((rv = strcmp(ap->name, bp->name)) != 0) return rv; if((rv = ap->mtime - bp->mtime) != 0) return rv; if((rv = strcmp(ap->type, bp->type)) != 0) return rv; if((rv = ap->size - bp->size) != 0) return rv; return 0; } /* file comparing function by date */ int comparebydate(const void *a, const void *b){ MYFILE *ap, *bp; int rv; ap = (MYFILE *)a; bp = (MYFILE *)b; if(!ap->type && !ap->type) return 0; if(!ap->type && bp->type) return 1; if(ap->type && !bp->type) return -1; if((rv = ap->mtime - bp->mtime) != 0) return rv; if((rv = strcmp(ap->name, bp->name)) != 0) return rv; if((rv = strcmp(ap->type, bp->type)) != 0) return rv; if((rv = ap->size - bp->size) != 0) return rv; return 0; } /* file comparing function by type */ int comparebytype(const void *a, const void *b){ MYFILE *ap, *bp; int rv; ap = (MYFILE *)a; bp = (MYFILE *)b; if(!ap->type && !ap->type) return 0; if(!ap->type && bp->type) return 1; if(ap->type && !bp->type) return -1; if((rv = strcmp(ap->type, bp->type)) != 0) return rv; if((rv = strcmp(ap->name, bp->name)) != 0) return rv; if((rv = ap->mtime - bp->mtime) != 0) return rv; if((rv = ap->size - bp->size) != 0) return rv; return 0; } /* file comparing function by size */ int comparebysize(const void *a, const void *b){ MYFILE *ap, *bp; int rv; ap = (MYFILE *)a; bp = (MYFILE *)b; if(!ap->type && !ap->type) return 0; if(!ap->type && bp->type) return 1; if(ap->type && !bp->type) return -1; if((rv = ap->size - bp->size) != 0) return rv; if((rv = strcmp(ap->name, bp->name)) != 0) return rv; if((rv = ap->mtime - bp->mtime) != 0) return rv; if((rv = strcmp(ap->type, bp->type)) != 0) return rv; return 0; } /* send the content of a file */ void sendasis(const char *url, int ims){ const char *path; char headbuf[SOCKBUFSIZ+32], buf[SOCKBUFSIZ]; int isdir, size, mtime, rfd, len; path = urltofile(url); if(!cbfilestat(path, &isdir, &size, &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(isdir){ sprintf(headbuf, "Location: http://%s:%d%s/\r\n", host, port, url); showerror(301, "Moved Parmanently", headbuf); return; } if((rfd = open(path, O_RDONLY, 0)) == -1){ 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); sprintf(headbuf, "Content-Length: %d\r\n", size); mywrite(clsock, headbuf, -1); sprintf(headbuf, "Content-Type: %s\r\n", gettype(path)); mywrite(clsock, headbuf, -1); mywrite(clsock, "\r\n", -1); if(clmtype != METHEAD){ while((len = read(rfd, buf, SOCKBUFSIZ)) > 0){ mywrite(clsock, buf, len); } } close(rfd); printf("%s: %s: %d: %s: 200\n", progname, claddr, clport, clqstr); fflush(stdout); } /* main routine for searching */ void searchmain(const char *query){ const char *tmp; char *phesc, *lbesc, *scesc, line[SOCKBUFSIZ], *pname, *pbuf; CBMAP *params; int i, psiz; /* read parameters */ phrase = NULL; max = 0; clshow = 0; clcode = 0; clall = FALSE; drep = 0; sort = NULL; expr = NULL; page = 0; skip = 0; unit = 0; relid = 0; detid = 0; tfidf = TRUE; mrglbl = NULL; showsc = FALSE; relsc = NULL; params = getparams(query); phrase = cbmapget(params, "phrase", -1, NULL); if((tmp = cbmapget(params, "max", -1, NULL)) != NULL) max = atoi(tmp); if((tmp = cbmapget(params, "clshow", -1, NULL)) != NULL) clshow = atoi(tmp); if((tmp = cbmapget(params, "clcode", -1, NULL)) != NULL) clcode = atoi(tmp); if((tmp = cbmapget(params, "clall", -1, NULL)) != NULL && !strcmp(tmp, "true")) clall = TRUE; if((tmp = cbmapget(params, "drep", -1, NULL)) != NULL) drep = atoi(tmp); sort = cbmapget(params, "sort", -1, NULL); expr = cbmapget(params, "expr", -1, NULL); if((tmp = cbmapget(params, "page", -1, NULL)) != NULL) page = atoi(tmp); if((tmp = cbmapget(params, "skip", -1, NULL)) != NULL) skip = atoi(tmp); if((tmp = cbmapget(params, "unit", -1, NULL)) != NULL) unit = atoi(tmp); if((tmp = cbmapget(params, "relid", -1, NULL)) != NULL) relid = atoi(tmp); if((tmp = cbmapget(params, "detid", -1, NULL)) != NULL) detid = atoi(tmp); if((tmp = cbmapget(params, "tfidf", -1, NULL)) != NULL && !strcmp(tmp, "false")) tfidf = FALSE; mrglbl = cbmapget(params, "mrglbl", -1, NULL); if((tmp = cbmapget(params, "showsc", -1, NULL)) != NULL && !strcmp(tmp, "true")) showsc = TRUE; relsc = cbmapget(params, "relsc", -1, NULL); if(!phrase) phrase = ""; if(max < 1) max = defmax; if(clshow < 0) clshow = 0; if(clcode < 0) clcode = 0; if(drep < 0) drep = 0; if(!sort) sort = "score"; if(!expr) expr = "asis"; if(page < 1) page = 1; if(skip < 1) skip = 0; if(unit < boolunit) unit = boolunit; if(relid < 1) relid = 0; if(detid < 1) detid = 0; if(!mrglbl) mrglbl = ""; if(!relsc) relsc = ""; if(drep > 0) skip = 0; if(relid > 0 || relsc[0] != '\0'){ sort = "score"; expr = "asis"; } phesc = NULL; lbesc = NULL; scesc = NULL; if((tmp = cbmapget(params, "enc", -1, NULL)) != NULL){ if((phesc = cbiconv(phrase, -1, tmp, ENCODING, NULL, NULL)) != NULL) phrase = phesc; if((lbesc = cbiconv(mrglbl, -1, tmp, ENCODING, NULL, NULL)) != NULL) mrglbl = lbesc; if((scesc = cbiconv(relsc, -1, tmp, ENCODING, NULL, NULL)) != NULL) relsc = scesc; } if(cbstrfwimatch(phrase, "[related] ")){ relid = atoi(strchr(phrase, ' ') + 1); phrase = ""; sort = "score"; expr = "asis"; } if(cbstrfwimatch(phrase, "[detail] ")){ detid = atoi(strchr(phrase, ' ') + 1); phrase = ""; } if(!scesc && cbstrfwimatch(phrase, "[relsc] ")){ scesc = cburldecode(strchr(phrase, ' ') + 1, NULL); relsc = scesc; phrase = ""; sort = "score"; expr = "asis"; } /* initialize the timer */ cbproctime(&ustime, &sstime); /* send HTTP headers */ mywrite(clsock, "HTTP/1.0 200 OK\r\n", -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); 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-Disposition: inline; filename=%s\r\n", SAVENAME); mywrite(clsock, line, -1); sprintf(line, "Content-Type: text/html; charset=%s\r\n", ENCODING); mywrite(clsock, line, -1); mywrite(clsock, "\r\n", -1); /* parse the template */ tabidx = 0; if(clmtype != METHEAD){ for(i = 0; i < cblistnum(tmpllist); i++){ tmp = cblistval(tmpllist, i, NULL); if(!strcmp(tmp, "")){ xmlprintf("
\n"); showform(); xmlprintf("
"); } else if(!strcmp(tmp, "")){ xmlprintf("
\n"); if(relid > 0 || relsc[0] != '\0'){ showrelated(); } else if(detid > 0){ showdetail(); } else if(phrase[0] != '\0'){ showboolean(); } else { xmlprintf("%s", toptext); } xmlprintf("
"); } else if(!strcmp(tmp, "")){ xmlprintf("
\n"); showinfo(); xmlprintf("
"); } else if(!strcmp(tmp, "")){ xmlprintf("
\n"); showproctime(); xmlprintf("
"); } else if(!strcmp(tmp, "")){ xmlprintf("
\n"); showversion(); xmlprintf("
"); } else if(cbstrfwmatch(tmp, "")){ xmlprintf(NULL); pname = cbmemdup(tmp + 12, strlen(tmp) - 15); if((pbuf = cbreadfile(pname, &psiz)) != NULL){ mywrite(clsock, pbuf, psiz); free(pbuf); } free(pname); } else if(cbstrfwmatch(tmp, "")){ xmlprintf(NULL); estputenv("ESTPHRASE", phrase); pname = cbmemdup(tmp + 12, strlen(tmp) - 15); if((pbuf = estreadexec(pname, &psiz)) != NULL){ mywrite(clsock, pbuf, psiz); free(pbuf); } free(pname); } else { while(*tmp != '\0'){ if(*tmp == '{'){ if(cbstrfwmatch(tmp, "{ESTPHRASE}")){ xmlprintf("%@", phrase); tmp += 11; } else if(cbstrfwmatch(tmp, "{ESTMAX}")){ xmlprintf("%d", max); tmp += 8; } else if(cbstrfwmatch(tmp, "{ESTDNUM}")){ xmlprintf("%d", oddnum(odeum)); tmp += 9; } else if(cbstrfwmatch(tmp, "{ESTWNUM}")){ xmlprintf("%d", odwnum(odeum)); tmp += 9; } else { xmlprintf("%c", *tmp); tmp++; } } else { xmlprintf("%c", *tmp); tmp++; } } } } } /* output log */ outputlog(); /* release resources */ free(scesc); free(lbesc); free(phesc); cbmapclose(params); printf("%s: %s: %d: %s: 200\n", progname, claddr, clport, clqstr); fflush(stdout); } /* get a map of the CGI parameters */ CBMAP *getparams(const char *query){ CBMAP *params; CBLIST *pairs; char *key, *val, *dkey, *dval; int i; params = cbmapopenex(ESTPETITBNUM); pairs = cbsplit(query, -1, "&"); for(i = 0; i < cblistnum(pairs); i++){ key = cbmemdup(cblistval(pairs, i, NULL), -1); if((val = strchr(key, '=')) != NULL){ *(val++) = '\0'; dkey = cburldecode(key, NULL); dval = cburldecode(val, NULL); cbmapput(params, dkey, -1, dval, -1, FALSE); free(dval); free(dkey); } free(key); } cblistclose(pairs); return params; } /* show index information */ void showform(void){ int i; xmlprintf("
\n", SEARCHURL); xmlprintf("
\n"); xmlprintf("\n"); if(relid > 0){ xmlprintf("\n", relid, ++tabidx); } else if(phrase[0] == '\0' && detid > 0){ xmlprintf("\n", detid, ++tabidx); } else if(relsc[0] != '\0'){ xmlprintf("\n", relsc, ++tabidx); } else { xmlprintf("\n", phrase, ++tabidx); } xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n", lsubmit, ++tabidx); xmlprintf("\n"); xmlprintf("
\n"); xmlprintf("
\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("\n"); xmlprintf("
\n"); xmlprintf("
\n"); xmlprintf(NULL); } /* show relational search result */ void showrelated(void){ ODDOC *doc, *tdoc; CBMAP *scores, *tsc, *dirs; CBLIST *lines, *mywords; CBDATUM *myphrase; ESTWORD *words; ODPAIR *pairs; const char *uri, *ruri, *word, *label, *ep; char *ubuf, *mbuf, *tmbuf, *tmp; int i, msiz, ulen, wsiz, wnum, pnum, lnum, tmsiz, show, step; int ovec[RELVECNUM], tvec[RELVECNUM]; /* get the document */ doc = NULL; mbuf = NULL; if(relsc[0] != '\0'){ if(!scdb){ 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"); xmlprintf("
%@:
\n", lresult); xmlprintf("
"); label = ldocs[0] != '\0' ? ldocs : mrglbl; if(label[0] != '\0') xmlprintf("%@:", label); if(ubuf[0] != '\0'){ xmlprintf("%@: %#", lrelto, ubuf, ubuf); xmlprintf(" = "); } xmlprintf("%d %@", pnum, lhits); xmlprintf("
\n"); xmlprintf("
\n"); if(pnum < 1) 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"); xmlprintf("
%@:
\n", lclusters); for(i = 0; i < clnum && (clall || i < clshow); i++){ xmlprintf("
"); xmlprintf(""); xmlprintf("(+)", SEARCHURL, phrase, max, drep, sort, expr, unit, relid, relsc, clshow, i + 1, clall ? "true" : "false"); xmlprintf(""); cbmapiterinit(clusts[i].scores); for(j = 0; j < clustkeys && (kbuf = cbmapiternext(clusts[i].scores, NULL)) != NULL; j++){ xmlprintf(" "); if(i == clcode - 1){ xmlprintf("%@", kbuf); } else { xmlprintf("%@", kbuf); } xmlprintf(""); } xmlprintf(" (%d)", cblistnum(clusts[i].ids)); xmlprintf("
\n"); } xmlprintf("
"); xmlprintf("= "); xmlprintf(""); xmlprintf("%d %@", ssum, lhits); if(!clall && clshow < clnum) xmlprintf(" %@", SEARCHURL, phrase, max, drep, sort, expr, unit, relid, relsc, clshow, clcode, lmore); xmlprintf(""); xmlprintf("
\n"); xmlprintf("
\n"); free(tpairs); for(i = 0; i < tnum; i++){ cbmapclose(tails[i]); } free(tails); if(clcode > 0 && clcode - 1 < clnum){ for(i = 0; i < cblistnum(clusts[clcode-1].ids); i++){ kbuf = cblistval(clusts[clcode-1].ids, i, NULL); pairs[i].id = atoi(kbuf); kbuf = strchr(kbuf, ':') + 1; pairs[i].score = atoi(kbuf); } *np = i; odpairssort(pairs, *np); } for(i = 0; i < clnum; i++){ cbmapclose(clusts[i].scores); cblistclose(clusts[i].ids); } free(clusts); } /* sum up clusters */ void sumupclusters(CLUSTER *clusts, int *np){ SCWORD *scws; CBMAP *scores; const char *kbuf, *vbuf; char numbuf[ESTNUMBUFSIZ]; int i, j, clnum, rcnt, hit, mi, si, ksiz, vsiz, pt, tvec[RELVECNUM]; double msc, sc; clnum = *np; for(rcnt = 0; clnum > clshow; rcnt++){ hit = FALSE; for(i = 0; i < clnum && clnum > clshow; i++){ if(rcnt < 2 && cbmaprnum(clusts[i].scores) < RELVECNUM) continue; mi = -1; msc = 0.0; for(j = 0; j < clnum; j++){ if(j == i) continue; settvec(clusts[i].scores, clusts[j].scores, tvec); sc = odvectorcosine(clusts[i].vector, tvec, RELVECNUM); if(mi < 0 || sc > msc){ mi = j; msc = sc; } } if(rcnt < 1 && msc < 0.95) continue; if(rcnt < 2 && msc < 0.90) continue; if(rcnt < 3 && msc < 0.85) continue; if(rcnt < 4 && msc < 0.80) continue; if(rcnt < 5 && msc < 0.75) continue; if(msc < 0.35) continue; if(msc < 0.70 && cblistnum(clusts[i].ids) + cblistnum(clusts[mi].ids) > (*np / clshow) * 2) continue; hit = TRUE; for(j = 0; j < cblistnum(clusts[mi].ids); j++){ kbuf = cblistval(clusts[mi].ids, j, &ksiz); cblistpush(clusts[i].ids, kbuf, ksiz); } cbmapiterinit(clusts[mi].scores); while((kbuf = cbmapiternext(clusts[mi].scores, &ksiz)) != NULL){ pt = 0; if((vbuf = cbmapget(clusts[i].scores, kbuf, ksiz, NULL)) != NULL) pt = atoi(vbuf); vsiz = sprintf(numbuf, "%d", pt + atoi(cbmapget(clusts[mi].scores, kbuf, ksiz, NULL))); cbmapput(clusts[i].scores, kbuf, ksiz, numbuf, vsiz, TRUE); } cbmapclose(clusts[mi].scores); cblistclose(clusts[mi].ids); clusts[i].point += clusts[mi].point; clnum--; if(mi != clnum) clusts[mi] = clusts[clnum]; si = i == clnum ? mi : i; scws = cbmalloc(cbmaprnum(clusts[si].scores) * sizeof(SCWORD) + 1); cbmapiterinit(clusts[si].scores); for(j = 0; (kbuf = cbmapiternext(clusts[si].scores, &ksiz)) != NULL; j++){ scws[j].kbuf = kbuf; scws[j].ksiz = ksiz; scws[j].point = atoi(cbmapget(clusts[si].scores, kbuf, ksiz, NULL)); } cbqsort(scws, cbmaprnum(clusts[si].scores), sizeof(SCWORD), scwordcompare); scores = cbmapopenex(RELVECNUM * 4); for(j = 0; j < cbmaprnum(clusts[si].scores); j++){ vsiz = sprintf(numbuf, "%d", scws[j].point); cbmapput(scores, scws[j].kbuf, scws[j].ksiz, numbuf, vsiz, TRUE); } cbmapclose(clusts[si].scores); clusts[si].scores = scores; setovec(clusts[si].scores, clusts[si].vector); free(scws); if(msc >= 0.80) i--; } if(!hit && rcnt >= 8) break; } cbqsort(clusts, clnum, sizeof(CLUSTER), clustercompare); *np = clnum; } /* compare two scores */ int scwordcompare(const void *a, const void *b){ SCWORD *ap, *bp; ap = (SCWORD *)a; bp = (SCWORD *)b; return bp->point - ap->point; } /* compare two clusters */ int clustercompare(const void *a, const void *b){ CLUSTER *ap, *bp; ap = (CLUSTER *)a; bp = (CLUSTER *)b; return bp->point - ap->point; } /* show detail of a document */ void showdetail(void){ ODDOC *doc; CBMAP *scores; ESTWORD *words; const char *uri, *ruri, *title, *author, *rcpt, *mcast, *date, *type, *enc, *size, *kbuf; char *ubuf, *mbuf; int wnum, id, ulen, msiz; /* get search words */ words = estsearchwords(phrase, &wnum, FALSE); /* get the document */ if(!(doc = odgetbyid(odeum, detid))){ 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"); xmlprintf("
%@:
\n", lidnum); xmlprintf("
%d
\n", id); xmlprintf("
%@:
\n", luri); xmlprintf("
%#
\n", ubuf, ubuf); if(title && strlen(title) > 0){ xmlprintf("
%@:
\n", ltitle); xmlprintf("
%@
\n", title); } if(author && strlen(author) > 0){ xmlprintf("
%@:
\n", lauthor); xmlprintf("
%@
\n", author); } if(rcpt && strlen(rcpt) > 0){ xmlprintf("
%@:
\n", lrcpt); xmlprintf("
%@
\n", rcpt); } if(mcast && strlen(mcast) > 0){ xmlprintf("
%@:
\n", lmcast); xmlprintf("
%@
\n", mcast); } if(date && strlen(date) > 0){ xmlprintf("
%@:
\n", ldate); xmlprintf("
%@
\n", date); } if(type && strlen(type) > 0){ xmlprintf("
%@:
\n", ltype); xmlprintf("
%@
\n", type); } if(enc && strlen(enc) > 0){ xmlprintf("
%@:
\n", lenc); xmlprintf("
%@
\n", enc); } if(size && strlen(size) > 0){ xmlprintf("
%@:
\n", lsize); xmlprintf("
%@
\n", size); } if(scdb && (mbuf = crget(scdb, (char *)&id, sizeof(int), 0, -1, &msiz)) != NULL){ scores = cbmapload(mbuf, msiz); cbmapiterinit(scores); xmlprintf("
%@:
\n", lkwords); xmlprintf("
\n"); while((kbuf = cbmapiternext(scores, NULL)) != NULL){ xmlprintf("%@ (%@)\n", kbuf, cbmapget(scores, kbuf, -1, NULL)); } xmlprintf("
\n"); cbmapclose(scores); free(mbuf); } xmlprintf("
\n"); xmlprintf("
\n"); xmlprintf("
%@:
\n", ltext); showsummary(doc, words, wnum, TRUE); xmlprintf("
\n"); /* release resouces */ free(ubuf); oddocclose(doc); estfreewords(words, wnum); } /* show boolean search result */ void showboolean(void){ ESTWORD *words; ODPAIR *pairs, pswap; ODDOC *doc; CBMAP *dirs; const char *dstr, *label; char *tmp; int i, wnum, myunit, date, ldnum, pnum, lnum, show, step; /* get search words */ words = estsearchwords(phrase, &wnum, strcmp(expr, "wild") && strcmp(expr, "regex")); if(wnum < 1){ 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"); xmlprintf("
%@:
\n", lresult); xmlprintf("
"); label = ldocs[0] != '\0' ? ldocs : mrglbl; if(label[0] != '\0') xmlprintf("%@:", label); ldnum = 0; for(i = 0; i < wnum; i++){ ldnum = words[i].dnum; if(i > 0){ xmlprintf(" %s ", i, words[i].type == ESTCONDAND ? "[and]" : words[i].type == ESTCONDOR ? "[or]" : "[not]"); } xmlprintf("%s" " (%d)", i, words[i].word, i, words[i].dnum); } xmlprintf(" = "); xmlprintf("%d %@", wnum < 2 ? ldnum : pnum, lhits); if(wnum > 1 && lnum > 0){ xmlprintf(" %@", SEARCHURL, phrase, max, drep, sort, expr, oddnum(odeum) * RESROOM, clshow, lmore); } xmlprintf(""); xmlprintf("
\n"); xmlprintf("
\n"); if(pnum < 1) 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("
\n", id); xmlprintf("
"); xmlprintf("%d: ", (page - 1) * max + show); if(altfunc[0] != '\0'){ xmlprintf("%@", altfunc, id, ++tabidx, title); } else { xmlprintf("" "%@", ubuf, ++tabidx, title); } if(label[0] != '\0') xmlprintf("(%@)", label); if(rel >= 0){ sprintf(numbuf, "%.2f", rel); xmlprintf(" (%@%%)", id, numbuf); } else { xmlprintf(" (%d pt.)", id, score); } xmlprintf("
\n"); showsummary(doc, words, wnum, FALSE); xmlprintf("
"); top = TRUE; if(author && author[0] != '\0'){ if(!top) xmlprintf(" "); xmlprintf("%@: %@", lauthor, author); top = FALSE; } if(date && date[0] != '\0'){ if(!top) xmlprintf(" "); xmlprintf("%@: %@", ldate, date); top = FALSE; } if(type && type[0] != '\0'){ if(!top) xmlprintf(" "); xmlprintf("%@: %@", ltype, type); top = FALSE; } if(enc && enc[0] != '\0'){ if(!top) xmlprintf(" "); xmlprintf("%@: %@", lenc, enc); top = FALSE; } if(size && size[0] != '\0'){ if(!top) xmlprintf(" "); xmlprintf("%@: %@", lsize, size); top = FALSE; } xmlprintf("
\n"); xmlprintf("
"); xmlprintf("%@: %$", luri, ubuf, id, id, ubuf); xmlprintf("
\n"); if(showsc){ if(scdb && (tmp = crget(scdb, (char *)&id, sizeof(int), 0, -1, &tsiz)) != NULL){ scores = cbmapload(tmp, tsiz); free(tmp); } else { scores = oddocscores(doc, ESTKEYNUM, NULL); } scbuf = cbdatumopen("", 0); cbmapiterinit(scores); while((key = cbmapiternext(scores, &ksiz)) != NULL){ val = cbmapget(scores, key, ksiz, &vsiz); cbdatumcat(scbuf, key, ksiz); cbdatumcat(scbuf, "\t", 1); cbdatumcat(scbuf, val, vsiz); cbdatumcat(scbuf, "\n", 1); } tmp = cburlencode(cbdatumptr(scbuf), cbdatumsize(scbuf)); xmlprintf("
%@
\n", tmp); free(tmp); cbdatumclose(scbuf); cbmapclose(scores); } xmlprintf("
\n"); free(ubuf); xmlprintf(NULL); } /* show summary of a document */ void showsummary(const ODDOC *doc, const ESTWORD *words, int wnum, int detail){ const CBLIST *nwords, *awords; CBMAP *kmap, *tmap, *scores; const char *evword, *normal, *asis, *ibuf, *kbuf; char *mbuf; int i, j, lnum, gshow, lshow, nwsiz, awsiz, cjk, tb, pv, bi, id, msiz, issc; xmlprintf("
"); nwords = oddocnwords(doc); awords = oddocawords(doc); kmap = cbmapopenex(RELVECNUM * 2); tmap = cbmapopenex(RELVECNUM * 2); if(words){ for(i = 0; i < wnum; i++){ if(words[i].type == ESTCONDNOT) continue; if(words[i].evwords){ for(j = 0; j < cblistnum(words[i].evwords); j++){ evword = cblistval(words[i].evwords, j, NULL); cbmapput(kmap, evword, -1, (char *)&i, sizeof(int), TRUE); cbmapput(tmap, evword, -1, (char *)&i, sizeof(int), TRUE); } } else { cbmapput(kmap, words[i].word, -1, (char *)&i, sizeof(int), TRUE); cbmapput(tmap, words[i].word, -1, (char *)&i, sizeof(int), TRUE); } } } lnum = cblistnum(nwords); gshow = 0; /* show top words */ lshow = 0; cjk = FALSE; for(i = 0; i < lnum && lshow < (detail ? lnum : words ? sumtop : sumall); i++){ normal = cblistval(nwords, i, &nwsiz); asis = cblistval(awords, i, &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(" ... "); /* 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("
\n"); } /* show paging navigation */ void showpaging(int next, int all){ xmlprintf("
"); xmlprintf(""); if(page > 1){ xmlprintf("%@", SEARCHURL, phrase, max, drep, sort, expr, unit, page - 1, relid, relsc, clshow, clcode, clall ? "true" : "false", ++tabidx, lprev); } else { xmlprintf("%@", lprev); } xmlprintf(""); xmlprintf(" "); xmlprintf(""); if(next < all){ xmlprintf("%@", SEARCHURL, phrase, max, drep, sort, expr, unit, next, page + 1, relid, relsc, clshow, clcode, clall ? "true" : "false", ++tabidx, lnext); } else { xmlprintf("%@", lnext); } xmlprintf(""); xmlprintf("
\n"); } /* show index information */ void showinfo(void){ xmlprintf("The index contains %d documents and %d words.\n", oddnum(odeum), odwnum(odeum)); } /* show process time information */ void showproctime(void){ double uetime, setime; char atbuf[ESTNUMBUFSIZ], utbuf[ESTNUMBUFSIZ], stbuf[ESTNUMBUFSIZ]; cbproctime(&uetime, &setime); sprintf(atbuf, "%.2f", (uetime - ustime) + (setime - sstime)); sprintf(utbuf, "%.2f", uetime - ustime); sprintf(stbuf, "%.2f", setime - sstime); xmlprintf("Processing time is %@ sec." " (user:%@ + sys:%@).\n", atbuf, utbuf, stbuf); } /* show version information */ void showversion(void){ xmlprintf(""); xmlprintf("Powered by Estraier version %@", _EST_VERSION); if(estisregex) xmlprintf(" (regex)"); if(ESTISSTRICT) xmlprintf(" (strict)"); if(ESTISNOSTOPW) xmlprintf(" (no-stopword)"); if(estiscjkuni) xmlprintf(" (cjkuni)"); if(estischasen) xmlprintf(" (chasen)"); if(estismecab) xmlprintf(" (mecab)"); if(estiskakasi) xmlprintf(" (kakasi)"); xmlprintf(".\n"); } /* output log */ void outputlog(void){ FILE *ofp; char date[ESTDATEBUFSIZ]; time_t t; struct tm *tp; if(logfile[0] == '\0') return; if(!(ofp = fopen(logfile, "ab"))) return; sprintf(date, "0000/00/00 00:00:00"); if((t = time(NULL)) != -1 && (tp = localtime(&t)) != NULL){ sprintf(date, "%04d/%02d/%02d %02d:%02d:%02d", tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec); } fprintf(ofp, "%s\t%s\t%s\t%d\t%d:%d\t%d\t%s\t%s\t%d\t%d\t%d\n", date, claddr, phrase, max, clshow, clcode, drep, sort, expr, page, relid, detid); fclose(ofp); } /* END OF FILE */