~ubuntu-branches/ubuntu/hardy/hyperestraier/hardy

« back to all changes in this revision

Viewing changes to estwaver.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Langasek
  • Date: 2006-11-14 05:28:32 UTC
  • mfrom: (2.1.4 feisty)
  • Revision ID: james.westby@ubuntu.com-20061114052832-0lzqzcefn8mt4yqe
Tags: 1.4.9-1.1
* Non-maintainer upload.
* High-urgency upload for RC bugfix.
* Set HOME=$(CURDIR)/junkhome when building, otherwise the package build
  will incorrectly look for headers there -- and fail when the directory
  exists and is unreadable, as happens sometimes on sudo-using
  autobuilders!

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*************************************************************************************************
 
2
 * The command line interface of web crawler
 
3
 *                                                      Copyright (C) 2004-2006 Mikio Hirabayashi
 
4
 * This file is part of Hyper Estraier.
 
5
 * Hyper Estraier is free software; you can redistribute it and/or modify it under the terms of
 
6
 * the GNU Lesser General Public License as published by the Free Software Foundation; either
 
7
 * version 2.1 of the License or any later version.  Hyper Estraier is distributed in the hope
 
8
 * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 
10
 * License for more details.
 
11
 * You should have received a copy of the GNU Lesser General Public License along with Hyper
 
12
 * Estraier; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 
13
 * Boston, MA 02111-1307 USA.
 
14
 *************************************************************************************************/
 
15
 
 
16
 
 
17
#include "wavermod.h"
 
18
 
 
19
#define SLEEPUSEC      100000            /* sleep time in micro seconds */
 
20
#define MINPRIOR       0.01              /* minimal priority of non-seed documents */
 
21
#define NODERTTNUM     5000              /* number of documents for node rotation */
 
22
 
 
23
enum {                                   /* enumeration for crawling modes */
 
24
  CM_CONTINUE,                           /* continue */
 
25
  CM_RESTART,                            /* restart */
 
26
  CM_REVISIT,                            /* revisit */
 
27
  CM_REVCONT                             /* revisit and continue */
 
28
};
 
29
 
 
30
typedef struct {                         /* type of structure for interaction of a URL */
 
31
  int thid;                              /* thread ID number */
 
32
  WAVER *waver;                          /* waver handle */
 
33
  char *url;                             /* URL */
 
34
  int depth;                             /* depth */
 
35
  int pid;                               /* ID number of the parent document */
 
36
  double psim;                           /* similarity of the parent document */
 
37
  time_t mdate;                          /* last-modified date */
 
38
} TARGSURL;
 
39
 
 
40
 
 
41
/* global variables */
 
42
pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;  /* global mutex */
 
43
int g_thnum = 0;                         /* number of running threads */
 
44
int g_thseq = 0;                         /* sequence of thread ID */
 
45
CBMAP *g_tasks = NULL;                   /* URLs of running tasks */
 
46
const char *g_progname;                  /* program name */
 
47
int g_sigterm = FALSE;                   /* flag for termination signal */
 
48
const char *g_pxhost = NULL;             /* host name of the proxy */
 
49
int g_pxport = 0;                        /* host name of the proxy */
 
50
int g_timeout = -1;                      /* timeout in seconds */
 
51
int g_inputlang = ESTLANGEN;             /* prefered language */
 
52
 
 
53
 
 
54
/* function prototypes */
 
55
int main(int argc, char **argv);
 
56
static void usage(void);
 
57
static void setsignals(void);
 
58
static void sigtermhandler(int num);
 
59
static char *dequeue(WAVER *waver, int *depthp, int *pidp, double *psimp);
 
60
static void enqueuelinks(WAVER *waver, const char *base, CBLIST *links, CBMAP *kwords,
 
61
                         int depth, int id, int pid, double psim);
 
62
static int runinit(int argc, char **argv);
 
63
static int runcrawl(int argc, char **argv);
 
64
static int rununittest(int argc, char **argv);
 
65
static int runfetch(int argc, char **argv);
 
66
static int procinit(const char *rootdir, int opts);
 
67
static int proccrawl(const char *rootdir, int mode);
 
68
static int procunittest(const char *rootdir);
 
69
static int procfetch(const char *url);
 
70
static int strtolang(const char *str);
 
71
static char *capitalize(const char *str);
 
72
static void seedurldocs(WAVER *waver, const char *url, int depth, double bias, CBMAP *ulinks);
 
73
static int accessthnum(int inc);
 
74
static int puttask(const char *url);
 
75
static void outtask(const char *url);
 
76
static void *geturldoc(void *args);
 
77
static char *urltosavepath(const char *savedir, const char *url);
 
78
 
 
79
 
 
80
/* main routine */
 
81
int main(int argc, char **argv){
 
82
  const char *tmp;
 
83
  int rv;
 
84
  if((tmp = getenv("ESTDBGFD")) != NULL) dpdbgfd = atoi(tmp);
 
85
  est_proc_env_reset();
 
86
  g_progname = argv[0];
 
87
  g_sigterm = FALSE;
 
88
  g_tasks = cbmapopenex(MINIBNUM);
 
89
  cbglobalgc(g_tasks, (void (*)(void *))cbmapclose);
 
90
  if(!est_init_net_env()){
 
91
    log_print(LL_ERROR, "could not initialize network environment");
 
92
    exit(1);
 
93
  }
 
94
  atexit(est_free_net_env);
 
95
  if(argc < 2) usage();
 
96
  rv = 0;
 
97
  if(!strcmp(argv[1], "init")){
 
98
    rv = runinit(argc, argv);
 
99
  } else if(!strcmp(argv[1], "crawl")){
 
100
    setsignals();
 
101
    rv = runcrawl(argc, argv);
 
102
  } else if(!strcmp(argv[1], "unittest")){
 
103
    rv = rununittest(argc, argv);
 
104
  } else if(!strcmp(argv[1], "fetch")){
 
105
    rv = runfetch(argc, argv);
 
106
  } else {
 
107
    usage();
 
108
  }
 
109
  return rv;
 
110
}
 
111
 
 
112
 
 
113
/* print the usage and exit */
 
114
static void usage(void){
 
115
  fprintf(stderr, "%s: command line interface of web crawler\n", g_progname);
 
116
  fprintf(stderr, "\n");
 
117
  fprintf(stderr, "usage:\n");
 
118
  fprintf(stderr, "  %s init [-xs|-xl|-xh] rootdir\n", g_progname);
 
119
  fprintf(stderr, "  %s crawl [-restart|-revisit|-revcont] rootdir\n", g_progname);
 
120
  fprintf(stderr, "  %s unittest rootdir\n", g_progname);
 
121
  fprintf(stderr, "  %s fetch [-proxy host port] [-tout num] [-il lang] url\n", g_progname);
 
122
  fprintf(stderr, "\n");
 
123
  exit(1);
 
124
}
 
125
 
 
126
 
 
127
/* set signal handlers */
 
128
static void setsignals(void){
 
129
  est_signal(1, sigtermhandler);
 
130
  est_signal(2, sigtermhandler);
 
131
  est_signal(3, sigtermhandler);
 
132
  est_signal(13, SIG_IGN);
 
133
  est_signal(14, SIG_IGN);
 
134
  est_signal(15, sigtermhandler);
 
135
  g_sigterm = FALSE;
 
136
}
 
137
 
 
138
 
 
139
/* handler of termination signal */
 
140
static void sigtermhandler(int num){
 
141
  g_sigterm = TRUE;
 
142
  fprintf(stderr, "%s: the termination signal %d catched\n", g_progname, num);
 
143
}
 
144
 
 
145
 
 
146
/* dequeue a URL from the priority queue (it has a critical section inside) */
 
147
static char *dequeue(WAVER *waver, int *depthp, int *pidp, double *psimp){
 
148
  char *buf, *rp, *rv;
 
149
  if(time(NULL) > waver->stime + waver->period) return NULL;
 
150
  if(pthread_mutex_lock(&g_mutex) != 0){
 
151
    log_print(LL_ERROR, "could not get mutex");
 
152
    return NULL;
 
153
  }
 
154
  if(waver->curnum > waver->docnum){
 
155
    pthread_mutex_unlock(&g_mutex);
 
156
    return NULL;
 
157
  }
 
158
  while(queue_rnum(waver->queue) < 1 && g_thnum > 0){
 
159
    pthread_mutex_unlock(&g_mutex);
 
160
    est_usleep(SLEEPUSEC);
 
161
    if(pthread_mutex_lock(&g_mutex) != 0){
 
162
      log_print(LL_ERROR, "could not get mutex");
 
163
      return NULL;
 
164
    }
 
165
  }
 
166
  rv = NULL;
 
167
  *depthp = 0;
 
168
  *pidp = 0;
 
169
  *psimp = 0.0;
 
170
  while(TRUE){
 
171
    if(!(buf = queue_dequeue(waver->queue))) break;
 
172
    if((rp = strchr(buf, '\t')) != NULL && (*depthp = atoi(buf)) >= 0){
 
173
      rp++;
 
174
      rv = cbmemdup(rp, -1);
 
175
      if((rp = strchr(buf, ':')) != NULL){
 
176
        rp++;
 
177
        *pidp = atoi(rp);
 
178
        if((rp = strchr(rp, ':')) != NULL){
 
179
          rp++;
 
180
          *psimp = strtod(rp, NULL);
 
181
        }
 
182
      }
 
183
      free(buf);
 
184
      break;
 
185
    }
 
186
    free(buf);
 
187
  }
 
188
  pthread_mutex_unlock(&g_mutex);
 
189
  return rv;
 
190
}
 
191
 
 
192
 
 
193
/* add traced URLs (it should be called in a critical section) */
 
194
static void enqueuelinks(WAVER *waver, const char *base, CBLIST *links, CBMAP *kwords,
 
195
                         int depth, int id, int pid, double psim){
 
196
  PMRULE *pmrule;
 
197
  CBMAP *pkwords, *ulinks;
 
198
  const char *vbuf;
 
199
  char numbuf[NUMBUFSIZ], *ubuf, *pv, *benc, *tenc;
 
200
  int i, j, vsiz, lnum, llen, slash, vnum, *svec, *tvec, num, allow;
 
201
  double similarity, lnumtune, depthtune, masstune, priority, remoteness;
 
202
  if(depth >= waver->maxdepth) return;
 
203
  if(!kwords || cbmaprnum(waver->kwords) < 1){
 
204
    similarity = psim * 0.7;
 
205
    psim = similarity;
 
206
  } else {
 
207
    vnum = waver->seedkeynum;
 
208
    svec = cbmalloc(vnum * sizeof(int));
 
209
    tvec = cbmalloc(vnum * sizeof(int));
 
210
    est_vector_set_seed(waver->kwords, svec, vnum);
 
211
    est_vector_set_target(waver->kwords, kwords, tvec, vnum);
 
212
    similarity = est_vector_cosine(svec, tvec, vnum) * 0.9 + 0.1;
 
213
    similarity = similarity * (1.0 - waver->inherit) + psim * waver->inherit;
 
214
    psim = similarity;
 
215
    free(tvec);
 
216
    free(svec);
 
217
    if(pid > 0 && (pkwords = est_mtdb_get_keywords(waver->index, pid))){
 
218
      vnum = waver->savekeynum;
 
219
      svec = cbmalloc(vnum * sizeof(int));
 
220
      tvec = cbmalloc(vnum * sizeof(int));
 
221
      est_vector_set_seed(pkwords, svec, vnum);
 
222
      est_vector_set_target(pkwords, kwords, tvec, vnum);
 
223
      similarity *= 1.0 - pow(est_vector_cosine(svec, tvec, vnum), 3.14) * 0.8;
 
224
      cbmapclose(pkwords);
 
225
      free(tvec);
 
226
      free(svec);
 
227
    }
 
228
  }
 
229
  if((pv = strstr(base, "://")) != NULL && (pv = strchr(pv + 3, '/')) != NULL){
 
230
    vbuf = cbmapget(waver->sites, base, pv - base + 1, NULL);
 
231
    num = (vbuf ? atoi(vbuf) : 0) + 1;
 
232
    sprintf(numbuf, "%d", num);
 
233
    cbmapput(waver->sites, base, pv - base + 1, numbuf, -1, TRUE);
 
234
    if(cbmaprnum(waver->sites) > (waver->queuesize / 3.0 + 1) * 1.4){
 
235
      log_print(LL_INFO, "site map sliming: %d", cbmaprnum(waver->sites));
 
236
      kwords_reduce(waver->sites, waver->queuesize / 3.0 + 1, TRUE);
 
237
    }
 
238
  }
 
239
  ulinks = cbmapopenex(MINIBNUM);
 
240
  cbmapput(ulinks, base, -1, "", 0, FALSE);
 
241
  lnum = cblistnum(links) + 4;
 
242
  lnumtune = pow(lnum, 0.7);
 
243
  depthtune = pow(depth + 7, 0.8);
 
244
  for(i = 0; i < cblistnum(links) && i < 1024; i++){
 
245
    vbuf = cblistval(links, i, &vsiz);
 
246
    ubuf = cbmemdup(vbuf, vsiz);
 
247
    if((pv = strchr(ubuf, '#')) != NULL) *pv = '\0';
 
248
    llen = strlen(ubuf);
 
249
    if(llen > 1024 || cbmapget(ulinks, ubuf, llen, NULL)){
 
250
      free(ubuf);
 
251
      continue;
 
252
    }
 
253
    cbmapput(ulinks, ubuf, -1, "", 0, FALSE);
 
254
    allow = FALSE;
 
255
    for(j = 0; j < cblistnum(waver->pmrules); j++){
 
256
      pmrule = (PMRULE *)cblistval(waver->pmrules, j, NULL);
 
257
      switch(pmrule->visit){
 
258
      case 1:
 
259
        if(est_regex_match(pmrule->regex, ubuf)) allow = TRUE;
 
260
        break;
 
261
      case -1:
 
262
        if(est_regex_match(pmrule->regex, ubuf)) allow = FALSE;
 
263
        break;
 
264
      }
 
265
    }
 
266
    if(!allow){
 
267
      free(ubuf);
 
268
      continue;
 
269
    }
 
270
    masstune = 1.0;
 
271
    if((pv = strstr(ubuf, "://")) != NULL && (pv = strchr(pv + 3, '/')) != NULL){
 
272
      vbuf = cbmapget(waver->sites, ubuf, pv - ubuf + 1, NULL);
 
273
      num = (vbuf ? atoi(vbuf) : 0) + 1;
 
274
      if(num > waver->masscheck) masstune /= sqrt((double)num / waver->masscheck);
 
275
    }
 
276
    slash = 6;
 
277
    for(pv = ubuf; *pv != '\0'; pv++){
 
278
      switch(*pv){
 
279
      case '/': slash += 1; break;
 
280
      case '?': slash += 5; break;
 
281
      case '&': slash += 1; break;
 
282
      case ';': slash += 1; break;
 
283
      }
 
284
    }
 
285
    remoteness = 8.0;
 
286
    benc = cbmemdup(base, -1);
 
287
    tenc = cbmemdup(ubuf, -1);
 
288
    if((pv = strchr(benc, '?')) != NULL) pv[0] = '\0';
 
289
    if((pv = strchr(tenc, '?')) != NULL) pv[0] = '\0';
 
290
    if(!strcmp(tenc, benc)){
 
291
      priority *= 0.7;
 
292
      remoteness = 1.5;
 
293
    } else {
 
294
      if((pv = strrchr(benc, '/')) != NULL) pv[1] = '\0';
 
295
      if((pv = strrchr(tenc, '/')) != NULL) pv[1] = '\0';
 
296
      if(cbstrfwmatch(tenc, benc)){
 
297
        priority *= 0.9;
 
298
        remoteness = 4.0;
 
299
      }
 
300
    }
 
301
    free(tenc);
 
302
    free(benc);
 
303
    switch(waver->strategy){
 
304
    case CS_BALANCED:
 
305
      priority = (similarity * 128 * masstune) / depthtune / lnumtune / slash;
 
306
      priority *= (lnum - (i / remoteness)) / lnum;
 
307
      if(llen > 80) priority /= pow(llen / 80.0, 0.7);
 
308
      break;
 
309
    case CS_SIMILARITY:
 
310
      priority = similarity;
 
311
      priority *= 0.9 + ((lnum - i) / (double)lnum) / 50;
 
312
      break;
 
313
    case CS_DEPTH:
 
314
      priority = (depth + 1) * 0.001;
 
315
      priority = priority > 1.0 ? 1.0 : priority;
 
316
      priority *= 0.9 + ((lnum - i) / (double)lnum) / 50;
 
317
      break;
 
318
    case CS_WIDTH:
 
319
      priority = 1.0 / (depth + 1);
 
320
      priority *= 0.9 + ((lnum - i) / (double)lnum) / 50;
 
321
      break;
 
322
    case CS_RANDOM:
 
323
      priority = (est_random() * 128 * masstune) / depthtune / lnumtune / slash;
 
324
      priority *= (lnum - (i / remoteness)) / lnum;
 
325
      if(llen > 80) priority /= pow(llen / 80.0, 0.7);
 
326
      break;
 
327
    default:
 
328
      priority = (128 * masstune) / depthtune / lnumtune / slash;
 
329
      priority *= (lnum - (i / remoteness)) / lnum;
 
330
      if(llen > 80) priority /= pow(llen / 80.0, 0.7);
 
331
      break;
 
332
    }
 
333
    tenc = cbsprintf("%d:%d:%.5f\t%s", depth + 1, id, psim, ubuf);
 
334
    queue_enqueue(waver->queue, tenc, 1.0 - priority);
 
335
    free(tenc);
 
336
    free(ubuf);
 
337
  }
 
338
  cbmapclose(ulinks);
 
339
  if(queue_rnum(waver->queue) > waver->queuesize * 1.4){
 
340
    log_print(LL_INFO, "queue sliming: %d", queue_rnum(waver->queue));
 
341
    if(!queue_slim(waver->queue, waver->queuesize))
 
342
      log_print(LL_ERROR, "queue sliming failed");
 
343
  }
 
344
}
 
345
 
 
346
 
 
347
/* parse arguments of the init command */
 
348
static int runinit(int argc, char **argv){
 
349
  char *rootdir;
 
350
  int i, opts, rv;
 
351
  rootdir = NULL;
 
352
  opts = 0;
 
353
  for(i = 2; i < argc; i++){
 
354
    if(!rootdir && argv[i][0] == '-'){
 
355
      if(!strcmp(argv[i], "-xs")){
 
356
        opts |= WI_SMALL;
 
357
      } else if(!strcmp(argv[i], "-xl")){
 
358
        opts |= WI_LARGE;
 
359
      } else if(!strcmp(argv[i], "-xh")){
 
360
        opts |= WI_HUGE;
 
361
      } else {
 
362
        usage();
 
363
      }
 
364
    } else if(!rootdir){
 
365
      rootdir = argv[i];
 
366
    } else {
 
367
      usage();
 
368
    }
 
369
  }
 
370
  if(!rootdir) usage();
 
371
  rv = procinit(rootdir, opts);
 
372
  return rv;
 
373
}
 
374
 
 
375
 
 
376
/* parse arguments of the crawl command */
 
377
static int runcrawl(int argc, char **argv){
 
378
  char *rootdir;
 
379
  int i, mode, rv;
 
380
  rootdir = NULL;
 
381
  mode = CM_CONTINUE;
 
382
  for(i = 2; i < argc; i++){
 
383
    if(!rootdir && argv[i][0] == '-'){
 
384
      if(!strcmp(argv[i], "-restart")){
 
385
        mode = CM_RESTART;
 
386
      } else if(!strcmp(argv[i], "-revisit")){
 
387
        mode = CM_REVISIT;
 
388
      } else if(!strcmp(argv[i], "-revcont")){
 
389
        mode = CM_REVCONT;
 
390
      } else {
 
391
        usage();
 
392
      }
 
393
    } else if(!rootdir){
 
394
      rootdir = argv[i];
 
395
    } else {
 
396
      usage();
 
397
    }
 
398
  }
 
399
  if(!rootdir) usage();
 
400
  rv = proccrawl(rootdir, mode);
 
401
  return rv;
 
402
}
 
403
 
 
404
 
 
405
/* parse arguments of the unittest command */
 
406
static int rununittest(int argc, char **argv){
 
407
  char *rootdir;
 
408
  int i, rv;
 
409
  rootdir = NULL;
 
410
  for(i = 2; i < argc; i++){
 
411
    if(!rootdir && argv[i][0] == '-'){
 
412
      usage();
 
413
    } else if(!rootdir){
 
414
      rootdir = argv[i];
 
415
    } else {
 
416
      usage();
 
417
    }
 
418
  }
 
419
  if(!rootdir) usage();
 
420
  rv = procunittest(rootdir);
 
421
  return rv;
 
422
}
 
423
 
 
424
 
 
425
/* parse arguments of the fetch command */
 
426
static int runfetch(int argc, char **argv){
 
427
  char *url;
 
428
  int i, rv;
 
429
  url = NULL;
 
430
  for(i = 2; i < argc; i++){
 
431
    if(!url && argv[i][0] == '-'){
 
432
      if(!strcmp(argv[i], "-proxy")){
 
433
        if(++i >= argc) usage();
 
434
        g_pxhost = argv[i];
 
435
        if(++i >= argc) usage();
 
436
        g_pxport = atoi(argv[i]);
 
437
      } else if(!strcmp(argv[i], "-tout")){
 
438
        if(++i >= argc) usage();
 
439
        g_timeout = atoi(argv[i]);
 
440
      } else if(!strcmp(argv[i], "-il")){
 
441
        if(++i >= argc) usage();
 
442
        g_inputlang = strtolang(argv[i]);
 
443
      } else {
 
444
        usage();
 
445
      }
 
446
    } else if(!url){
 
447
      url = argv[i];
 
448
    } else {
 
449
      usage();
 
450
    }
 
451
  }
 
452
  if(!url) usage();
 
453
  rv = procfetch(url);
 
454
  return rv;
 
455
}
 
456
 
 
457
 
 
458
/* perform the init command */
 
459
static int procinit(const char *rootdir, int opts){
 
460
  if(!waver_init(rootdir, opts)){
 
461
    log_print(LL_ERROR, "initializing the root directory failed");
 
462
    return 1;
 
463
  }
 
464
  log_open(rootdir, LOGFILE, LL_INFO, FALSE);
 
465
  log_print(LL_INFO, "the root directory created");
 
466
  return 0;
 
467
}
 
468
 
 
469
 
 
470
/* perform the init command */
 
471
static int proccrawl(const char *rootdir, int mode){
 
472
  pthread_t th;
 
473
  TARGSURL *targs;
 
474
  WAVER *waver;
 
475
  CBDATUM *kwbuf;
 
476
  CBMAP *ulinks;
 
477
  const char *kbuf, *rp;
 
478
  char *url, *rec, *tmp, *endurl;
 
479
  int i, err, depth, pid, thid, locked, ended;
 
480
  double psim;
 
481
  time_t t, mdate;
 
482
  if(!(waver = waver_open(rootdir))){
 
483
    log_print(LL_ERROR, "%s: could not open", rootdir);
 
484
    return 1;
 
485
  }
 
486
  err = FALSE;
 
487
  switch(mode){
 
488
  default:
 
489
    log_print(LL_INFO, "crawling started (continue)");
 
490
    break;
 
491
  case CM_RESTART:
 
492
    log_print(LL_INFO, "crawling started (restart)");
 
493
    break;
 
494
  case CM_REVISIT:
 
495
    log_print(LL_INFO, "crawling started (revisit)");
 
496
    break;
 
497
  case CM_REVCONT:
 
498
    log_print(LL_INFO, "crawling started (revcont)");
 
499
    break;
 
500
  }
 
501
  if(mode == CM_RESTART){
 
502
    while((tmp = queue_dequeue(waver->queue)) != NULL){
 
503
      free(tmp);
 
504
    }
 
505
  }
 
506
  endurl = NULL;
 
507
  if(mode == CM_REVISIT || mode == CM_REVCONT){
 
508
    t = time(NULL);
 
509
    criterinit(waver->trace);
 
510
    while((url = criternext(waver->trace, NULL)) != NULL){
 
511
      if((rec = crget(waver->trace, url, -1, 0, -1, NULL)) != NULL){
 
512
        if(est_mtdb_uri_to_id(waver->index, url) > 0){
 
513
          mdate = (time_t)strtod(rec, NULL);
 
514
          depth = 0;
 
515
          pid = 0;
 
516
          psim = 0.0;
 
517
          if((rp = strchr(rec, ':')) != NULL){
 
518
            rp++;
 
519
            depth = atoi(rp);
 
520
            if((rp = strchr(rp, ':')) != NULL){
 
521
              rp++;
 
522
              pid = atoi(rp);
 
523
              if((rp = strchr(rp, ':')) != NULL){
 
524
                rp++;
 
525
                psim = strtod(rp, NULL);
 
526
              }
 
527
            }
 
528
          }
 
529
          tmp = cbsprintf("%d:%d:%.5f\t%s", depth, pid, psim, url);
 
530
          queue_enqueue(waver->queue, tmp, (mdate / t) * MINPRIOR);
 
531
          free(tmp);
 
532
        }
 
533
        free(rec);
 
534
      }
 
535
      free(endurl);
 
536
      endurl = url;
 
537
    }
 
538
  }
 
539
  if(mode == CM_RESTART){
 
540
    criterinit(waver->trace);
 
541
    while((url = criternext(waver->trace, NULL)) != NULL){
 
542
      crout(waver->trace, url, -1);
 
543
      free(url);
 
544
    }
 
545
  }
 
546
  cbmapiterinit(waver->seeds);
 
547
  ulinks = cbmapopen();
 
548
  while((kbuf = cbmapiternext(waver->seeds, NULL)) != NULL){
 
549
    seedurldocs(waver, kbuf, 0, strtod(cbmapget(waver->seeds, kbuf, -1, NULL), NULL), ulinks);
 
550
  }
 
551
  cbmapclose(ulinks);
 
552
  kwords_reduce(waver->kwords, waver->seedkeynum, FALSE);
 
553
  kwbuf = cbdatumopen(NULL, -1);
 
554
  cbmapiterinit(waver->kwords);
 
555
  for(i = 0; (kbuf = cbmapiternext(waver->kwords, NULL)) != NULL; i++){
 
556
    if(i > 0) est_datum_printf(kwbuf, ", ");
 
557
    est_datum_printf(kwbuf, "%s (%s)", kbuf, cbmapget(waver->kwords, kbuf, -1, NULL));
 
558
  }
 
559
  log_print(LL_DEBUG, "seed keywords: %s", cbdatumptr(kwbuf));
 
560
  cbdatumclose(kwbuf);
 
561
  ended = FALSE;
 
562
  thid = 0;
 
563
  while(!g_sigterm && !ended && (tmp = dequeue(waver, &depth, &pid, &psim)) != NULL){
 
564
    if(endurl && !strcmp(tmp, endurl)){
 
565
      est_usleep(SLEEPUSEC);
 
566
      if(mode == CM_REVISIT){
 
567
        ended = TRUE;
 
568
      } else {
 
569
        log_print(LL_INFO, "waiting for threads: %d", accessthnum(0));
 
570
        t = time(NULL);
 
571
        while(accessthnum(0) > 0 && time(NULL) < t + waver->timeout * 2 + 1){
 
572
          est_usleep(SLEEPUSEC);
 
573
        }
 
574
        free(endurl);
 
575
        endurl = NULL;
 
576
      }
 
577
    }
 
578
    mdate = 0;
 
579
    if(pthread_mutex_lock(&g_mutex) == 0){
 
580
      if((rec = crget(waver->trace, tmp, -1, 0, -1, NULL)) != NULL){
 
581
        mdate = (time_t)strtod(rec, NULL);
 
582
        free(rec);
 
583
      }
 
584
      pthread_mutex_unlock(&g_mutex);
 
585
      if(mdate + waver->revisit >= time(NULL)){
 
586
        log_print(LL_DEBUG, "not modified: %s", tmp);
 
587
        free(tmp);
 
588
        continue;
 
589
      }
 
590
    } else {
 
591
      log_print(LL_ERROR, "could not get mutex");
 
592
    }
 
593
    if(cbmaprnum(waver->nodes) > 0 &&
 
594
       (waver->curnode < 1 || waver->minload >= 1.0 || thid % NODERTTNUM == 0 ||
 
595
        (thid % (NODERTTNUM / 10) == 0 && waver_current_node_load(waver) > 0.85))){
 
596
      waver_set_current_node(waver);
 
597
      log_print(LL_INFO, "current node changed: %d: %f", waver->curnode, waver->minload);
 
598
    }
 
599
    thid++;
 
600
    targs = cbmalloc(sizeof(TARGSURL));
 
601
    targs->thid = thid;
 
602
    targs->waver = waver;
 
603
    targs->url = tmp;
 
604
    targs->depth = depth;
 
605
    targs->pid = pid;
 
606
    targs->psim = psim;
 
607
    targs->mdate = mdate;
 
608
    if(waver->thnum > 1){
 
609
      while(accessthnum(0) >= waver->thnum){
 
610
        est_usleep(SLEEPUSEC);
 
611
      }
 
612
      if(pthread_create(&th, NULL, geturldoc, targs) == 0){
 
613
        pthread_detach(th);
 
614
        if(thid <= waver->thnum) est_usleep(SLEEPUSEC);
 
615
      } else {
 
616
        geturldoc(targs);
 
617
      }
 
618
    } else {
 
619
      geturldoc(targs);
 
620
    }
 
621
    if(thid % 256 == 0){
 
622
      locked = pthread_mutex_lock(&g_mutex) == 0;
 
623
      log_print(LL_INFO, "status: dnum=%d, wnum=%d, size=%.0f, queue=%d",
 
624
                est_mtdb_doc_num(waver->index), est_mtdb_word_num(waver->index),
 
625
                est_mtdb_size(waver->index), queue_rnum(waver->queue) + 1);
 
626
      if(locked) pthread_mutex_unlock(&g_mutex);
 
627
      est_usleep(SLEEPUSEC);
 
628
    }
 
629
  }
 
630
  est_usleep(SLEEPUSEC);
 
631
  if(waver->thnum > 1){
 
632
    log_print(LL_INFO, "waiting for threads: %d", accessthnum(0));
 
633
    t = time(NULL);
 
634
    while(accessthnum(0) > 0){
 
635
      if(time(NULL) > t + waver->timeout * 8 + 60){
 
636
        log_print(LL_WARN, "thread waiting timed out: %d", accessthnum(0));
 
637
        raise(3);
 
638
        est_usleep(1000 * 1000 * 5);
 
639
        break;
 
640
      }
 
641
      est_usleep(SLEEPUSEC);
 
642
    }
 
643
  }
 
644
  free(endurl);
 
645
  log_print(LL_INFO, "crawling finished");
 
646
  locked = pthread_mutex_lock(&g_mutex) == 0;
 
647
  g_sigterm = TRUE;
 
648
  if(!waver_close(waver)){
 
649
    log_print(LL_ERROR, "%s: closing failed", rootdir);
 
650
    err = TRUE;
 
651
  }
 
652
  if(locked) pthread_mutex_unlock(&g_mutex);
 
653
  if(!err) log_print(LL_INFO, "finished successfully");
 
654
  return err ? 1 : 0;
 
655
}
 
656
 
 
657
 
 
658
/* perform the unittest command */
 
659
static int procunittest(const char *rootdir){
 
660
  WAVER *waver;
 
661
  QUEUE *queue;
 
662
  CBMAP *seeds, *kwords;
 
663
  char uri[URIBUFSIZ], *vbuf;
 
664
  int i, err;
 
665
  log_print(LL_INFO, "initializing the waver handle");
 
666
  if(!waver_init(rootdir, 0)){
 
667
    log_print(LL_ERROR, "%s: initializing failed", rootdir);
 
668
    return FALSE;
 
669
  }
 
670
  log_print(LL_INFO, "opening the waver handle");
 
671
  if(!(waver = waver_open(rootdir))){
 
672
    log_print(LL_ERROR, "%s: opening failed", rootdir);
 
673
    return FALSE;
 
674
  }
 
675
  err = FALSE;
 
676
  log_print(LL_INFO, "checking seeding");
 
677
  seeds = waver->seeds;
 
678
  for(i = 0; i < 100; i++){
 
679
    sprintf(uri, "http://%05d/%x/%x.html",
 
680
            i + 1, (int)(est_random() * 0x1000000), (int)(est_random() * 0x1000000));
 
681
    cbmapput(seeds, uri, -1, "", 0, TRUE);
 
682
  }
 
683
  log_print(LL_INFO, "checking priority queue");
 
684
  queue = waver->queue;
 
685
  for(i = 0; i < 100; i++){
 
686
    sprintf(uri, "0:0:0.0\thttp://%05d/%x/%x.html",
 
687
            i + 1, (int)(est_random() * 0x1000000), (int)(est_random() * 0x1000000));
 
688
    if(!queue_enqueue(queue, uri, est_random())){
 
689
      err = TRUE;
 
690
      break;
 
691
    }
 
692
    if(i % 10 == 0) queue_set_range(queue, i);
 
693
  }
 
694
  if(queue_rnum(queue) != 100) err = TRUE;
 
695
  if(err) log_print(LL_ERROR, "%s: enqueue failed", rootdir);
 
696
  if(!queue_slim(queue, 60)) err = TRUE;
 
697
  if(err) log_print(LL_ERROR, "%s: slim failed", rootdir);
 
698
  for(i = 0; (vbuf = queue_dequeue(queue)) != NULL; i++){
 
699
    free(vbuf);
 
700
  }
 
701
  if(i != 60){
 
702
    err = TRUE;
 
703
    log_print(LL_ERROR, "%s: dequeue failed", rootdir);
 
704
  }
 
705
  log_print(LL_INFO, "checking keyword map");
 
706
  kwords = waver->kwords;
 
707
  for(i = 0; i < 10000; i++){
 
708
    sprintf(uri, "%d", (int)(est_random() * 1000));
 
709
    kwords_add(kwords, uri, (int)(est_random() * 1000));
 
710
  }
 
711
  kwords_reduce(kwords, 100, TRUE);
 
712
  log_print(LL_INFO, "closing the waver handle");
 
713
  if(!waver_close(waver)){
 
714
    log_print(LL_ERROR, "%s: closing failed", rootdir);
 
715
    err = TRUE;
 
716
  }
 
717
  if(!err) log_print(LL_INFO, "finished successfully");
 
718
  return err ? 1 : 0;
 
719
}
 
720
 
 
721
 
 
722
/* perform the fetch command */
 
723
static int procfetch(const char *url){
 
724
  CBMAP *heads;
 
725
  CBLIST *links;
 
726
  CBDATUM *raw;
 
727
  ESTDOC *doc;
 
728
  const char *border, *vbuf;
 
729
  char *str;
 
730
  int i, code, vsiz;
 
731
  raw = cbdatumopen(NULL, -1);
 
732
  heads = cbmapopen();
 
733
  links = cblistopen();
 
734
  doc = est_doc_new();
 
735
  if(!fetch_document(url, g_pxhost, g_pxport, g_timeout, -1, NULL, NULL, &code, raw, heads,
 
736
                     links, NULL, doc, g_inputlang)){
 
737
    log_print(LL_WARN, "could not get: %d: %s", code, url);
 
738
    est_doc_delete(doc);
 
739
    cblistclose(links);
 
740
    cbmapclose(heads);
 
741
    cbdatumclose(raw);
 
742
    return 1;
 
743
  }
 
744
  border = est_border_str();
 
745
  printf("URL: %s\r\n", url);
 
746
  str = cbdatestrhttp(time(NULL), 0);
 
747
  printf("Date: %s\r\n", str);
 
748
  free(str);
 
749
  str = cbmimeencode((vbuf = est_doc_attr(doc, ESTDATTRTITLE)) ? vbuf : url, "UTF-8", TRUE);
 
750
  printf("Subject: [estwaver] %s\r\n", str);
 
751
  free(str);
 
752
  printf("Content-Type: multipart/mixed; boundary=%s\r\n", border);
 
753
  printf("\r\n");
 
754
  printf("This is a multi-part message in MIME format.\n");
 
755
  printf("\r\n");
 
756
  printf("--%s\r\n", border);
 
757
  printf("Content-Type: text/x-estraier-draft\r\n");
 
758
  printf("X-Estwaver-Role: draft\r\n");
 
759
  printf("\r\n");
 
760
  str = est_doc_dump_draft(doc);
 
761
  printf("%s", str);
 
762
  free(str);
 
763
  printf("\r\n");
 
764
  printf("--%s\r\n", border);
 
765
  printf("Content-Type: text/plain\r\n");
 
766
  printf("X-Estwaver-Role: links\r\n");
 
767
  printf("\r\n");
 
768
  for(i = 0; i < cblistnum(links); i++){
 
769
    printf("%s\n", cblistval(links, i, NULL));
 
770
  }
 
771
  printf("\r\n");
 
772
  printf("--%s\r\n", border);
 
773
  cbmapiterinit(heads);
 
774
  while((vbuf = cbmapiternext(heads, &vsiz)) != NULL){
 
775
    if(vsiz < 1){
 
776
      printf("X-Original-HTTP-Response: %s\r\n", cbmapget(heads, vbuf, vsiz, NULL));
 
777
    } else if(!strcmp(vbuf, "content-encoding")){
 
778
      printf("X-Original-Content-Encoding: %s\r\n", cbmapget(heads, vbuf, vsiz, NULL));
 
779
    } else {
 
780
      str = capitalize(vbuf);
 
781
      printf("%s: %s\r\n", str, cbmapget(heads, vbuf, vsiz, NULL));
 
782
      free(str);
 
783
    }
 
784
  }
 
785
  printf("X-Estwaver-Role: raw\r\n");
 
786
  printf("\r\n");
 
787
  fwrite(cbdatumptr(raw), 1, cbdatumsize(raw), stdout);
 
788
  printf("\r\n");
 
789
  printf("--%s--\r\n", border);
 
790
  est_doc_delete(doc);
 
791
  cblistclose(links);
 
792
  cbmapclose(heads);
 
793
  cbdatumclose(raw);
 
794
  return 0;
 
795
}
 
796
 
 
797
 
 
798
/* get the language value */
 
799
static int strtolang(const char *str){
 
800
  if(!cbstricmp(str, "en")) return ESTLANGEN;
 
801
  if(!cbstricmp(str, "ja")) return ESTLANGJA;
 
802
  if(!cbstricmp(str, "zh")) return ESTLANGZH;
 
803
  if(!cbstricmp(str, "ko")) return ESTLANGKO;
 
804
  return ESTLANGMISC;
 
805
}
 
806
 
 
807
 
 
808
/* make a capitalized string */
 
809
static char *capitalize(const char *str){
 
810
  char *buf;
 
811
  int i, cap;
 
812
  buf = cbmemdup(str, -1);
 
813
  cap = TRUE;
 
814
  for(i = 0; buf[i] != '\0'; i++){
 
815
    if(cap && buf[i] >= 'a' && buf[i] <= 'z'){
 
816
      buf[i] -= 'a' - 'A';
 
817
    }
 
818
    cap = buf[i] == ' ' || buf[i] == '-';
 
819
  }
 
820
  return buf;
 
821
}
 
822
 
 
823
 
 
824
/* get keywords of a seed document */
 
825
static void seedurldocs(WAVER *waver, const char *url, int depth, double bias, CBMAP *ulinks){
 
826
  PMRULE *pmrule;
 
827
  ESTDOC *doc;
 
828
  CBMAP *kwords;
 
829
  CBLIST *links;
 
830
  const char *kbuf;
 
831
  char *ubuf, *pv;
 
832
  int i, j, code, ksiz, num, len, allow;
 
833
  double lnumtune;
 
834
  if(g_sigterm) return;
 
835
  ubuf = cbsprintf("%d:0:1.0\t%s", depth, url);
 
836
  queue_enqueue(waver->queue, ubuf, depth * MINPRIOR);
 
837
  free(ubuf);
 
838
  links = cblistopen();
 
839
  doc = est_doc_new();
 
840
  log_print(LL_INFO, "fetching: %d: %s", depth, url);
 
841
  fetch_document(url, waver->pxhost, waver->pxport, waver->timeout * 2, 0,
 
842
                 waver->urlrules, waver->mtrules, &code, NULL, NULL, links,
 
843
                 waver->unrules, doc, waver->language);
 
844
  if(code == 200 && !est_doc_is_empty(doc)){
 
845
    log_print(LL_INFO, "seeding: %.3f: %s", bias, url);
 
846
    kwords = est_morph_etch_doc(doc, waver->seedkeynum);
 
847
    cbmapiterinit(kwords);
 
848
    while((kbuf = cbmapiternext(kwords, &ksiz)) != NULL){
 
849
      num = atoi(cbmapget(kwords, kbuf, ksiz, NULL)) * bias;
 
850
      if(num > 0) kwords_add(waver->kwords, kbuf, num);
 
851
    }
 
852
    if(depth < waver->seeddepth && bias > 0.0){
 
853
      lnumtune = pow(cblistnum(links) + 2, 0.5);
 
854
      for(i = 0; i < cblistnum(links); i++){
 
855
        ubuf = cbmemdup(cblistval(links, i, NULL), -1);
 
856
        if((pv = strchr(ubuf, '#')) != NULL) *pv = '\0';
 
857
        len = strlen(ubuf);
 
858
        if(len > 1024 || cbmapget(ulinks, ubuf, len, NULL) ||
 
859
           cbmapget(waver->seeds, ubuf, len, NULL)){
 
860
          free(ubuf);
 
861
          continue;
 
862
        }
 
863
        cbmapput(ulinks, ubuf, -1, "", 0, FALSE);
 
864
        allow = FALSE;
 
865
        for(j = 0; j < cblistnum(waver->pmrules); j++){
 
866
          pmrule = (PMRULE *)cblistval(waver->pmrules, j, NULL);
 
867
          switch(pmrule->visit){
 
868
          case 1:
 
869
            if(est_regex_match(pmrule->regex, ubuf)) allow = TRUE;
 
870
            break;
 
871
          case -1:
 
872
            if(est_regex_match(pmrule->regex, ubuf)) allow = FALSE;
 
873
            break;
 
874
          }
 
875
        }
 
876
        if(allow) seedurldocs(waver, ubuf, depth + 1, bias / lnumtune, ulinks);
 
877
        free(ubuf);
 
878
      }
 
879
    }
 
880
    cbmapclose(kwords);
 
881
  } else {
 
882
    log_print(LL_INFO, "ignored: %d: %s", code, url);
 
883
  }
 
884
  est_doc_delete(doc);
 
885
  cblistclose(links);
 
886
}
 
887
 
 
888
 
 
889
/* access the number of threads */
 
890
static int accessthnum(int inc){
 
891
  static pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
 
892
  int thnum;
 
893
  if(pthread_mutex_lock(&mymutex) != 0){
 
894
    log_print(LL_ERROR, "could not get mutex");
 
895
    return INT_MAX;
 
896
  }
 
897
  g_thnum += inc;
 
898
  thnum = g_thnum;
 
899
  pthread_mutex_unlock(&mymutex);
 
900
  return thnum;
 
901
}
 
902
 
 
903
 
 
904
/* put the current task */
 
905
static int puttask(const char *url){
 
906
  if(pthread_mutex_lock(&g_mutex) != 0){
 
907
    log_print(LL_ERROR, "could not get mutex");
 
908
    return FALSE;
 
909
  }
 
910
  if(!cbmapput(g_tasks, url, -1, "", 0, FALSE)){
 
911
    pthread_mutex_unlock(&g_mutex);
 
912
    return FALSE;
 
913
  }
 
914
  pthread_mutex_unlock(&g_mutex);
 
915
  return TRUE;
 
916
}
 
917
 
 
918
 
 
919
/* put the current task */
 
920
static void outtask(const char *url){
 
921
  if(pthread_mutex_lock(&g_mutex) != 0){
 
922
    log_print(LL_ERROR, "could not get mutex");
 
923
    return;
 
924
  }
 
925
  cbmapout(g_tasks, url, -1);
 
926
  pthread_mutex_unlock(&g_mutex);
 
927
  return;
 
928
}
 
929
 
 
930
 
 
931
/* get a document of URL */
 
932
static void *geturldoc(void *args){
 
933
  TARGSURL *myargs;
 
934
  PMRULE *pmrule;
 
935
  WAVER *waver;
 
936
  CBMAP *heads, *kwords;
 
937
  CBLIST *links;
 
938
  CBDATUM *raw;
 
939
  ESTDOC *doc;
 
940
  char *url, *rec, *dpath, *epath;
 
941
  char numbuf[NUMBUFSIZ], *tmp;
 
942
  int i, thid, id, alive, code, allow;
 
943
  double now;
 
944
  accessthnum(1);
 
945
  myargs = args;
 
946
  thid = myargs->thid;
 
947
  waver = myargs->waver;
 
948
  url = myargs->url;
 
949
  if(!puttask(url)){
 
950
    log_print(LL_DEBUG, "[%d]: early collision: %s", thid, url);
 
951
    free(url);
 
952
    free(myargs);
 
953
    accessthnum(-1);
 
954
    return NULL;
 
955
  }
 
956
  if(waver->interval > 0) est_usleep(waver->interval * 1000);
 
957
  alive = FALSE;
 
958
  raw = cbdatumopen(NULL, -1);
 
959
  heads = cbmapopen();
 
960
  links = cblistopen();
 
961
  doc = est_doc_new();
 
962
  log_print(LL_INFO, "[%d]: fetching: %d: %s", thid, myargs->depth, url);
 
963
  fetch_document(url, waver->pxhost, waver->pxport, waver->timeout, myargs->mdate,
 
964
                 waver->urlrules, waver->mtrules, &code, raw, heads, links,
 
965
                 waver->unrules, doc, waver->language);
 
966
  if(g_sigterm){
 
967
    log_print(LL_WARN, "[%d]: terminated: %s", thid, url);
 
968
    if(pthread_mutex_lock(&g_mutex) == 0){
 
969
      tmp = cbsprintf("%d:%d:%.5f\t%s", myargs->depth, myargs->pid, myargs->psim, url);
 
970
      queue_enqueue(waver->queue, tmp, MINPRIOR);
 
971
      free(tmp);
 
972
      pthread_mutex_unlock(&g_mutex);
 
973
    }
 
974
    est_doc_delete(doc);
 
975
    cblistclose(links);
 
976
    cbmapclose(heads);
 
977
    cbdatumclose(raw);
 
978
    outtask(url);
 
979
    free(url);
 
980
    free(myargs);
 
981
    accessthnum(-1);
 
982
    return NULL;
 
983
  }
 
984
  if(pthread_mutex_lock(&g_mutex) == 0){
 
985
    now = time(NULL);
 
986
    if((rec = crget(waver->trace, url, -1, 0, -1, NULL)) != NULL){
 
987
      if(strtod(rec, NULL) + waver->revisit >= now){
 
988
        log_print(LL_DEBUG, "[%d]: late collision: %s", thid, url);
 
989
        free(rec);
 
990
        pthread_mutex_unlock(&g_mutex);
 
991
        est_doc_delete(doc);
 
992
        cblistclose(links);
 
993
        cbmapclose(heads);
 
994
        cbdatumclose(raw);
 
995
        outtask(url);
 
996
        free(url);
 
997
        free(myargs);
 
998
        accessthnum(-1);
 
999
        return NULL;
 
1000
      }
 
1001
      free(rec);
 
1002
    }
 
1003
    sprintf(numbuf, "%.0f:%d:%d:%.5f#%d",
 
1004
            now, myargs->depth, myargs->pid, myargs->psim, waver->curnode);
 
1005
    crput(waver->trace, url, -1, numbuf, -1, CR_DOVER);
 
1006
    if(code == 200 && est_doc_attr(doc, ESTDATTRURI)){
 
1007
      alive = TRUE;
 
1008
      est_doc_slim(doc, waver->textlimit);
 
1009
      kwords = est_morph_etch_doc(doc, waver->seedkeynum);
 
1010
      id = 0;
 
1011
      allow = FALSE;
 
1012
      for(i = 0; i < cblistnum(waver->pmrules); i++){
 
1013
        pmrule = (PMRULE *)cblistval(waver->pmrules, i, NULL);
 
1014
        switch(pmrule->index){
 
1015
        case 1:
 
1016
          if(est_regex_match(pmrule->regex, url)) allow = TRUE;
 
1017
          break;
 
1018
        case -1:
 
1019
          if(est_regex_match(pmrule->regex, url)) allow = FALSE;
 
1020
          break;
 
1021
        }
 
1022
      }
 
1023
      if(allow && !est_doc_is_empty(doc)){
 
1024
        est_doc_set_keywords(doc, kwords);
 
1025
        kwords_reduce(est_doc_keywords(doc), waver->savekeynum, FALSE);
 
1026
        if(waver->curnode > 0){
 
1027
          if(waver_node_put_doc(waver, doc, &code)){
 
1028
            log_print(LL_DEBUG, "[%d]: registered: %s", thid, url);
 
1029
            waver->curnum++;
 
1030
          } else {
 
1031
            log_print(LL_ERROR, "[%d]: registration failed: %s: %d", thid, url, code);
 
1032
          }
 
1033
        } else {
 
1034
          if(est_mtdb_put_doc(waver->index, doc, ESTPDCLEAN)){
 
1035
            log_print(LL_DEBUG, "[%d]: registered: %s", thid, url);
 
1036
            id = est_doc_id(doc);
 
1037
            waver->curnum++;
 
1038
          } else {
 
1039
            log_print(LL_ERROR, "[%d]: registration failed: %s: %s",
 
1040
                      thid, url, dperrmsg(dpecode));
 
1041
          }
 
1042
        }
 
1043
      } else {
 
1044
        log_print(LL_DEBUG, "[%d]: not to be indexed: %s", thid, url);
 
1045
      }
 
1046
      enqueuelinks(waver, url, links, kwords, myargs->depth, id, myargs->pid, myargs->psim);
 
1047
      cbmapclose(kwords);
 
1048
    } else if(cblistnum(links) > 0){
 
1049
      enqueuelinks(waver, url, links, NULL,
 
1050
                   myargs->depth, myargs->pid, myargs->pid, myargs->psim);
 
1051
      log_print(LL_INFO, "[%d]: redirected: %d: %s", thid, code, url);
 
1052
    } else {
 
1053
      log_print(LL_INFO, "[%d]: ignored: %d: %s", thid, code, url);
 
1054
    }
 
1055
    if(!alive && code != 304){
 
1056
      if(cbmaprnum(waver->nodes) > 0){
 
1057
        if(waver_node_out_doc(waver, url, &code)){
 
1058
          log_print(LL_DEBUG, "[%d]: deleted: %s", thid, url);
 
1059
        } else {
 
1060
          if(code != 400) log_print(LL_ERROR, "[%d]: deletion failed: %s: %d", thid, url, code);
 
1061
        }
 
1062
      } else if((id = est_mtdb_uri_to_id(waver->index, url)) > 0){
 
1063
        if(est_mtdb_out_doc(waver->index, id, ESTODCLEAN)){
 
1064
          log_print(LL_DEBUG, "[%d]: deleted: %s", thid, url);
 
1065
        } else {
 
1066
          log_print(LL_ERROR, "[%d]: deletion failed: %s: %s",
 
1067
                    thid, url, dperrmsg(dpecode));
 
1068
        }
 
1069
      }
 
1070
    }
 
1071
    pthread_mutex_unlock(&g_mutex);
 
1072
  } else {
 
1073
    log_print(LL_ERROR, "[%d]: could not get mutex", thid);
 
1074
  }
 
1075
  if(code == 200){
 
1076
    dpath = NULL;
 
1077
    epath = NULL;
 
1078
    if(waver->postproc){
 
1079
      dpath = cbsprintf("%s%c%s%c%08d.est",
 
1080
                        waver->rootdir, ESTPATHCHR, MYTMPDIR, ESTPATHCHR, thid);
 
1081
      epath = cbsprintf("%s%c%s%c%08d.dat",
 
1082
                        waver->rootdir, ESTPATHCHR, MYTMPDIR, ESTPATHCHR, thid);
 
1083
    }
 
1084
    if(waver->draftdir){
 
1085
      free(dpath);
 
1086
      dpath = urltosavepath(waver->draftdir, url);
 
1087
    }
 
1088
    if(waver->entitydir){
 
1089
      free(epath);
 
1090
      epath = urltosavepath(waver->entitydir, url);
 
1091
    }
 
1092
    if(dpath){
 
1093
      log_print(LL_DEBUG, "[%d]: saving: %s", thid, dpath);
 
1094
      tmp = est_doc_dump_draft(doc);
 
1095
      if(!cbwritefile(dpath, tmp, -1))
 
1096
        log_print(LL_ERROR, "[%d]: saving failed: %s", thid, dpath);
 
1097
      free(tmp);
 
1098
    }
 
1099
    if(epath){
 
1100
      log_print(LL_DEBUG, "[%d]: saving: %s", thid, epath);
 
1101
      if(!cbwritefile(epath, cbdatumptr(raw), cbdatumsize(raw)))
 
1102
        log_print(LL_ERROR, "[%d]: saving failed: %s", thid, epath);
 
1103
    }
 
1104
    if(waver->postproc){
 
1105
      tmp = cbsprintf("%s \"%s\" \"%s\"", waver->postproc, dpath, epath);
 
1106
      system(tmp);
 
1107
      free(tmp);
 
1108
    }
 
1109
    if(epath && !waver->entitydir) unlink(epath);
 
1110
    if(dpath && !waver->draftdir) unlink(dpath);
 
1111
    free(dpath);
 
1112
    free(epath);
 
1113
  }
 
1114
  est_doc_delete(doc);
 
1115
  cblistclose(links);
 
1116
  cbmapclose(heads);
 
1117
  cbdatumclose(raw);
 
1118
  outtask(url);
 
1119
  free(url);
 
1120
  free(myargs);
 
1121
  accessthnum(-1);
 
1122
  return NULL;
 
1123
}
 
1124
 
 
1125
 
 
1126
/* get the saving path of a URL */
 
1127
static char *urltosavepath(const char *savedir, const char *url){
 
1128
  CBDATUM *buf;
 
1129
  CBLIST *elems;
 
1130
  const char *rp;
 
1131
  int i;
 
1132
  if((rp = strstr(url, "://")) != NULL) url = rp + 3;
 
1133
  buf = cbdatumopen(NULL, -1);
 
1134
  elems = cbsplit(url, -1, "/");
 
1135
  if(cbstrbwmatch(url, "/")) cblistpush(elems, "index.html", -1);
 
1136
  est_datum_printf(buf, "%s", savedir);
 
1137
  for(i = 0; i < cblistnum(elems); i++){
 
1138
    rp = cblistval(elems, i, NULL);
 
1139
    if(rp[0] == '\0') continue;
 
1140
    est_mkdir(cbdatumptr(buf));
 
1141
    unlink(cbdatumptr(buf));
 
1142
    est_datum_printf(buf, "%c%?", ESTPATHCHR, rp);
 
1143
  }
 
1144
  cblistclose(elems);
 
1145
  return cbdatumtomalloc(buf, NULL);
 
1146
}
 
1147
 
 
1148
 
 
1149
 
 
1150
/* END OF FILE */