msgclass.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <ctype.h>
  5. #include "msgdb.h"
  6. void
  7. usage(void)
  8. {
  9. fprint(2, "usage: upas/msgclass [-a] [-d name dbfile]... [-l lockfile] [-m mul] [-t thresh] [tokenfile ...]\n");
  10. exits("usage");
  11. }
  12. enum
  13. {
  14. MAXBEST = 32,
  15. MAXLEN = 64,
  16. MAXTAB = 256,
  17. };
  18. typedef struct Ndb Ndb;
  19. struct Ndb
  20. {
  21. char *name;
  22. char *file;
  23. Msgdb *db;
  24. double p;
  25. long nmsg;
  26. };
  27. typedef struct Word Word;
  28. struct Word
  29. {
  30. char s[MAXLEN];
  31. int count[MAXTAB];
  32. double p[MAXTAB];
  33. double mp;
  34. int mi; /* w.p[w.mi] = w.mp */
  35. int nmsg;
  36. };
  37. Ndb db[MAXTAB];
  38. int ndb;
  39. int add;
  40. int mul;
  41. Msgdb *indb;
  42. Word best[MAXBEST];
  43. int mbest = 15;
  44. int nbest;
  45. void process(Biobuf*, char*);
  46. void lockfile(char*);
  47. void
  48. noteword(Word *w, char *s)
  49. {
  50. int i;
  51. for(i=nbest-1; i>=0; i--)
  52. if(w->mp < best[i].mp)
  53. break;
  54. i++;
  55. if(i >= mbest)
  56. return;
  57. if(nbest == mbest)
  58. nbest--;
  59. if(i < nbest)
  60. memmove(&best[i+1], &best[i], (nbest-i)*sizeof(best[0]));
  61. best[i] = *w;
  62. strecpy(best[i].s, best[i].s+MAXLEN, s);
  63. nbest++;
  64. }
  65. void
  66. main(int argc, char **argv)
  67. {
  68. int i, bad, m, tot, nn, j;
  69. Biobuf bin, *b, bout;
  70. char *s, *lf;
  71. double totp, p, thresh;
  72. long n;
  73. Word w;
  74. lf = nil;
  75. thresh = 0;
  76. ARGBEGIN{
  77. case 'a':
  78. add = 1;
  79. break;
  80. case 'd':
  81. if(ndb >= MAXTAB)
  82. sysfatal("too many db classes");
  83. db[ndb].name = EARGF(usage());
  84. db[ndb].file = EARGF(usage());
  85. ndb++;
  86. break;
  87. case 'l':
  88. lf = EARGF(usage());
  89. break;
  90. case 'm':
  91. mul = atoi(EARGF(usage()));
  92. break;
  93. case 't':
  94. thresh = atof(EARGF(usage()));
  95. break;
  96. default:
  97. usage();
  98. }ARGEND
  99. if(ndb == 0){
  100. fprint(2, "must have at least one -d option\n");
  101. usage();
  102. }
  103. indb = mdopen(nil, 1);
  104. if(argc == 0){
  105. Binit(&bin, 0, OREAD);
  106. process(&bin, "<stdin>");
  107. Bterm(&bin);
  108. }else{
  109. bad = 0;
  110. for(i=0; i<argc; i++){
  111. if((b = Bopen(argv[i], OREAD)) == nil){
  112. fprint(2, "opening %s: %r\n", argv[i]);
  113. bad = 1;
  114. continue;
  115. }
  116. process(b, argv[i]);
  117. Bterm(b);
  118. }
  119. if(bad)
  120. exits("open inputs");
  121. }
  122. lockfile(lf);
  123. bad = 0;
  124. for(i=0; i<ndb; i++){
  125. if((db[i].db = mdopen(db[i].file, 0)) == nil){
  126. fprint(2, "opendb %s: %r\n", db[i].file);
  127. bad = 1;
  128. }
  129. db[i].nmsg = mdget(db[i].db, "*From*");
  130. }
  131. if(bad)
  132. exits("open databases");
  133. /* run conditional probabilities of input words, getting 15 most specific */
  134. mdenum(indb);
  135. nbest = 0;
  136. while(mdnext(indb, &s, &n) >= 0){
  137. tot = 0;
  138. totp = 0.0;
  139. for(i=0; i<ndb; i++){
  140. nn = mdget(db[i].db, s)*(i==0 ? 3 : 1);
  141. tot += nn;
  142. w.count[i] = nn;
  143. p = w.count[i]/(double)db[i].nmsg;
  144. if(p >= 1.0)
  145. p = 1.0;
  146. w.p[i] = p;
  147. totp += p;
  148. }
  149. //fprint(2, "%s tot %d totp %g\n", s, tot, totp);
  150. if(tot < 2)
  151. continue;
  152. w.mp = 0.0;
  153. for(i=0; i<ndb; i++){
  154. p = w.p[i];
  155. p /= totp;
  156. if(p < 0.001)
  157. p = 0.001;
  158. else if(p > 0.999)
  159. p = 0.999;
  160. if(p > w.mp){
  161. w.mp = p;
  162. w.mi = i;
  163. }
  164. w.p[i] = p;
  165. }
  166. noteword(&w, s);
  167. }
  168. /* compute conditional probabilities of message classes using 15 most specific */
  169. totp = 0.0;
  170. for(i=0; i<ndb; i++){
  171. p = 1.0;
  172. for(j=0; j<nbest; j++)
  173. p *= best[j].p[i];
  174. db[i].p = p;
  175. totp += p;
  176. }
  177. for(i=0; i<ndb; i++)
  178. db[i].p /= totp;
  179. m = 0;
  180. for(i=1; i<ndb; i++)
  181. if(db[i].p > db[m].p)
  182. m = i;
  183. Binit(&bout, 1, OWRITE);
  184. if(db[m].p < thresh)
  185. m = -1;
  186. if(m >= 0)
  187. Bprint(&bout, "%s", db[m].name);
  188. else
  189. Bprint(&bout, "inconclusive");
  190. for(j=0; j<ndb; j++)
  191. Bprint(&bout, " %s=%g", db[j].name, db[j].p);
  192. Bprint(&bout, "\n");
  193. for(i=0; i<nbest; i++){
  194. Bprint(&bout, "%s", best[i].s);
  195. for(j=0; j<ndb; j++)
  196. Bprint(&bout, " %s=%g", db[j].name, best[i].p[j]);
  197. Bprint(&bout, "\n");
  198. }
  199. Bprint(&bout, "%s %g\n", best[i].s, best[i].p[m]);
  200. Bterm(&bout);
  201. if(m >= 0 && add){
  202. mdenum(indb);
  203. while(mdnext(indb, &s, &n) >= 0)
  204. mdput(db[m].db, s, mdget(db[m].db, s)+n*mul);
  205. mdclose(db[m].db);
  206. }
  207. exits(nil);
  208. }
  209. void
  210. process(Biobuf *b, char*)
  211. {
  212. char *s;
  213. char *p;
  214. long n;
  215. while((s = Brdline(b, '\n')) != nil){
  216. s[Blinelen(b)-1] = 0;
  217. if((p = strrchr(s, ' ')) != nil){
  218. *p++ = 0;
  219. n = atoi(p);
  220. }else
  221. n = 1;
  222. mdput(indb, s, mdget(indb, s)+n);
  223. }
  224. }
  225. int tpid;
  226. void
  227. killtickle(void)
  228. {
  229. postnote(PNPROC, tpid, "die");
  230. }
  231. void
  232. lockfile(char *s)
  233. {
  234. int fd, t, w;
  235. char err[ERRMAX];
  236. if(s == nil)
  237. return;
  238. w = 50;
  239. t = 0;
  240. for(;;){
  241. fd = open(s, OREAD);
  242. if(fd >= 0)
  243. break;
  244. rerrstr(err, sizeof err);
  245. if(strstr(err, "file is locked") == nil)
  246. break;
  247. sleep(w);
  248. t += w;
  249. if(w < 1000)
  250. w = (w*3)/2;
  251. if(t > 120*1000)
  252. break;
  253. }
  254. if(fd < 0)
  255. sysfatal("could not lock %s", s);
  256. switch(tpid = fork()){
  257. case -1:
  258. sysfatal("fork: %r");
  259. case 0:
  260. for(;;){
  261. sleep(30*1000);
  262. free(dirfstat(fd));
  263. }
  264. _exits(nil);
  265. default:
  266. break;
  267. }
  268. close(fd);
  269. atexit(killtickle);
  270. }