1 /* Copyright(C) 2004-2007 Brazil
2 
3   This library is free software; you can redistribute it and/or
4   modify it under the terms of the GNU Lesser General Public
5   License as published by the Free Software Foundation; either
6   version 2.1 of the License, or (at your option) any later version.
7 
8   This library is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   Lesser General Public License for more details.
12 
13   You should have received a copy of the GNU Lesser General Public
14   License along with this library; if not, write to the Free Software
15   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16 */
17 
18 #include "lib/inv.h"
19 #include "lib/sym.h"
20 #include "lib/store.h"
21 
22 #include <stdio.h>
23 #include <signal.h>
24 #include <string.h>
25 #include <setjmp.h>
26 #ifdef HAVE_UCONTEXT_H
27 #include <ucontext.h>
28 #endif /* HAVE_UCONTEXT_H */
29 
30 #ifdef WIN32
31 #include <errno.h>
32 #define sigsetjmp(x,y) setjmp((x))
33 #define siglongjmp(x,y) longjmp((x), (y))
34 typedef jmp_buf sigjmp_buf;
35 #endif /* WIN32 */
36 
37 /* TODO: export sen_index_clear_lock */
38 sen_rc sen_index_clear_lock(sen_index *i);
39 
40 #define INITIAL_CHECK_FILES 128
41 #define MAX_NSEGV 32
42 #define MAX_NERR_FILE 8
43 
44 #define CHK_DUMP_LEXICON (1 << 0)
45 #define CHK_THROUGH_SEGV (1 << 1)
46 #define CHK_UNLOCK       (1 << 2)
47 
48 typedef enum {
49   type_unknown = 0,
50   type_sym,
51   type_inv,
52   type_ja,
53   type_ra
54 } senchk_filetype;
55 
56 typedef enum {
57   rc_success
58 } senchk_rc;
59 
60 typedef struct {
61   senchk_filetype type;
62   char *path;
63   char *lexpath; /* for use inv check */
64   char *keyspath; /* for use inv check */
65   volatile unsigned int cerr; /* error count */
66 } senchk_file;
67 
68 typedef struct {
69   senchk_file *files;
70   unsigned int n_files;
71   unsigned int max_n_files;
72 } senchk_filelist;
73 
74 /* globals */
75 volatile unsigned int segvcount = 0;
76 senchk_file *chkfile = NULL;
77 int chkflags = 0;
78 sigjmp_buf sigret;
79 
80 static void
81 #ifdef WIN32
trapsegv(int sno)82 trapsegv(int sno)
83 #else /* WIN32 */
84 trapsegv(unsigned int sno, siginfo_t si, ucontext_t *sc)
85 #endif /* WIN32 */
86 {
87   if (++segvcount >= MAX_NSEGV) {
88     exit(1);
89   }
90 #ifndef WIN32
91   switch(si.si_code) {
92 #ifdef SEGV_MAPERR
93     case SEGV_MAPERR:
94       printf("* SEGV:%p IS NOT MAPPED!\n", si.si_addr);
95       break;
96 #endif /* SEGV_MAPERR */
97 #ifdef SEGV_ACCERR
98     case SEGV_ACCERR:
99       printf("* SEGV:CANNOT ACCESS %p\n", si.si_addr);
100       break;
101 #endif /* SEGV_ACCERR */
102     default:
103       printf("* SEGMENTATION FAULT! si_code:%d address:%p\n",
104              si.si_code, si.si_addr);
105   }
106 #endif /* WIN32 */
107   if (chkfile) {
108     printf("* ERROR occured on checking file '%s'. Maybe broken.\n", chkfile->path);
109     if (chkfile->keyspath && chkfile->lexpath) {
110       printf("* file '%s' depends keys file '%s' and lexicon file '%s'\n",
111              chkfile->path, chkfile->keyspath, chkfile->lexpath);
112     }
113     chkfile->cerr++;
114     siglongjmp(sigret, 1);
115   }
116 }
117 
118 static void
show_delimiter(FILE * fp)119 show_delimiter(FILE *fp)
120 {
121   fprintf(fp, "------------------------------------------------------\n");
122 }
123 
124 static void
show_sym_info(sen_sym * sym,FILE * fp)125 show_sym_info(sen_sym *sym, FILE *fp)
126 {
127   /* TODO: show flags */
128   /* TODO: need sen_enc2str */
129   int keysz;
130   sen_encoding enc;
131   unsigned int flags, nrec, fsz;
132   if (sen_sym_info(sym, &keysz, &flags, &enc, &nrec, &fsz)) {
133     fprintf(stderr, "sen_sym_info failed...\n");
134   } else {
135     fprintf(fp,
136             "  Sym file infomation:\n"
137             "   - key_size                : %10d\n"
138             "   - flags                   : %10u\n"
139             "   - encoding                : %10s\n"
140             "   - nrecords                : %10u\n"
141             "   - file_size               : %10u\n",
142             keysz, flags, sen_enctostr(enc), nrec, fsz);
143   }
144 }
145 
146 int
check_sym(senchk_file * f)147 check_sym(senchk_file *f)
148 {
149   sen_sym *sym = NULL;
150 
151   chkfile = f;
152   show_delimiter(stdout);
153   printf(" check sym file: '%s'\n", f->path);
154 
155   if (chkflags & CHK_THROUGH_SEGV || !sigsetjmp(sigret, 1)) {
156     sen_id id = SEN_SYM_NIL, pid, max_id, c = 0;
157 
158     if (!(sym = sen_sym_open(f->path))) {
159       printf("cannot open sym file '%s'\n", f->path);
160       return -1;
161     }
162     show_sym_info(sym, stdout);
163 
164     max_id = sen_sym_curr_id(sym);
165     while ((pid = sen_sym_next(sym, id)) != SEN_SYM_NIL && f->cerr < MAX_NERR_FILE) {
166       char key[SEN_SYM_MAX_KEY_SIZE];
167       sen_id at_id;
168       id = pid;
169       if (!(c % 1000)) {
170         fprintf(stderr, "entries: %d\r", c);
171       }
172       if (!sen_sym_key(sym, id, key, SEN_SYM_MAX_KEY_SIZE)) {
173         printf("sen_sym_key failed! id: %d\n", id);
174         f->cerr++;
175         continue;
176       } else if ((at_id = sen_sym_at(sym, key)) == SEN_SYM_NIL) {
177         printf("sen_sym_at failed! key: %s\n", key);
178         f->cerr++;
179       } else if (id != at_id) {
180         printf("sen_sym_key/sen_sym_at results are not consistent:"
181                "key: %s key_id:%d at_id:%d", key, id, at_id);
182         f->cerr++;
183       }
184       c++;
185       /* TODO: sis check with suffix_search */
186     }
187     sen_sym_close(sym);
188     printf(" checked sym file %s: total %d entries\n", f->path, c);
189 
190     return f->cerr;
191   } else {
192     /* handle segv */
193     if (sym) { sen_sym_close(sym); }
194     return -2;
195   }
196 }
197 
198 static void
show_index_info(sen_index * i,FILE * fp)199 show_index_info(sen_index *i, FILE *fp)
200 {
201   int keysz, flags, inseg;
202   sen_encoding enc;
203   unsigned int nkey, szkey, nlex, szlex;
204   unsigned long long szseg, szchunk;
205   if (sen_index_info(i, &keysz, &flags, &inseg, &enc,
206                      &nkey, &szkey, &nlex, &szlex, &szseg, &szchunk)) {
207     fprintf(stderr, "sen_index_info failed...\n");
208     return;
209   }
210   /* TODO: get lock infomation through API */
211   /* TODO: need sen_enc2str */
212   fprintf(fp,
213           "  Index file infomation:\n"
214           "   - lock                    : %12u\n"
215           "   - key_size                : %12d\n"
216           "   - flags                   : %12d\n"
217           "   - initial_n_segments      : %12d\n"
218           "   - encoding                : %12s\n"
219           "   - nrecords_keys           : %12u\n"
220           "   - file_size_keys          : %12u\n"
221           "   - nrecords_lexicon        : %12u\n"
222           "   - file_size_lexicon       : %12u\n"
223           "   - inv_segment_size        : %12llu\n"
224           "   - inv_chunk_size          : %12llu\n",
225           *i->keys->lock, keysz, flags, inseg,
226           sen_enctostr(enc), nkey, szkey,
227           nlex, szlex, szseg, szchunk);
228 }
229 
230 int
check_inv(senchk_file * f)231 check_inv(senchk_file *f)
232 {
233   int e;
234   sen_index *i = NULL;
235   sen_sym *keys = NULL, *lex = NULL;
236 
237   chkfile = f;
238   show_delimiter(stdout);
239   printf(" check inv file: '%s'\n", f->path);
240 
241   if (chkflags & CHK_THROUGH_SEGV || !sigsetjmp(sigret, 1)) {
242     sen_id tid = SEN_SYM_NIL, c = 0;
243     sen_inv *inv;
244     sen_inv_cursor *cur;
245     unsigned int tdf = 0, tsf = 0, tnposts = 0;
246 
247     if (!(keys = sen_sym_open(f->keyspath))) {
248       printf("cannot open keys sym file '%s' "
249              "for checking inv file '%s'\n", f->keyspath, f->path);
250       e = -1; goto exit;
251     }
252     if (!(lex = sen_sym_open(f->lexpath))) {
253       printf("cannot open lex sym file '%s' "
254              "for checking inv file '%s'\n", f->lexpath, f->path);
255       e = -2; goto exit;
256     }
257     if (!(i = sen_index_open_with_keys_lexicon(f->path, keys, lex))) {
258       printf("cannot open inv file '%s'\n", f->path);
259       e = -3; goto exit;
260     }
261     show_index_info(i, stdout);
262 
263     if (chkflags & CHK_UNLOCK) {
264       printf("  unlock inv file: '%s'\n", f->path);
265       e = sen_index_clear_lock(i) == sen_success ? 0 : -5;
266     } else {
267       inv = i->inv;
268       if (chkflags & CHK_DUMP_LEXICON) {
269         puts("  Inv records infomation:\n"
270              "         tid,        df,        sf,    nposts | term");
271       }
272       while ((tid = sen_sym_next(lex, tid)) != SEN_SYM_NIL && f->cerr < MAX_NERR_FILE) {
273         if (!(c % 1000)) {
274           fprintf(stderr, "entries: %d\r", c);
275         }
276         if (!(cur = sen_inv_cursor_open(inv, tid, 1))) {
277           /* TODO: detect array_at fails or not
278           printf("cannot open inv cursor '%s'\n", f->path);
279           f->cerr++;
280           */
281         } else {
282           sen_inv_posting pre = {0, 0, 0, 0, 0, 0};
283           unsigned int df = 0, sf = 0, nposts = 0;
284           while (!sen_inv_cursor_next(cur) && f->cerr < MAX_NERR_FILE) {
285             sen_inv_posting *post = cur->post;
286             sen_id max_rid = sen_sym_curr_id(i->keys);
287             if (pre.rid > post->rid || post->rid > max_rid) {
288               printf("invalid rid: pre-rid:%d rid:%d max_rid:%d\n",
289                      pre.rid, post->rid, max_rid);
290               f->cerr++;
291             } else if (pre.rid == post->rid) {
292               /* TODO: get max_sid and compare it */
293               if (pre.sid >= post->sid) {
294                 printf("invalid sid: rid:%d pre-sid:%d sid:%d\n",
295                        post->rid, pre.sid, post->sid);
296                 f->cerr++;
297               } else {
298                 sf++;
299               }
300             } else {
301               char key[SEN_SYM_MAX_KEY_SIZE];
302               if (!sen_sym_key(keys, post->rid, key, SEN_SYM_MAX_KEY_SIZE)) {
303                 printf("looking up key from keys failed.\n"
304                        " rid: %d\n", post->rid);
305                 f->cerr++;
306               }
307               df++;
308               sf++;
309             }
310             pre.pos = 0;
311             while (!sen_inv_cursor_next_pos(cur) && f->cerr < MAX_NERR_FILE) {
312               post = cur->post;
313               if (pre.pos && pre.pos >= post->pos) {
314                 printf("invalid position: rid:%d sid:%d pre-pos:%d pos:%d\n",
315                        post->rid, post->sid, pre.pos, post->pos);
316                 f->cerr++;
317               } else if (!post->tf) {
318                 printf("invalid tf: rid:%d sid:%d pos:%d tf:0\n",
319                        post->rid, post->sid, post->pos);
320                 f->cerr++;
321               }
322               nposts++;
323               pre = *post;
324             }
325             pre = *post;
326           }
327           if (chkflags & CHK_DUMP_LEXICON) {
328             char term[SEN_SYM_MAX_KEY_SIZE];
329             if (!sen_sym_key(lex, tid, term, SEN_SYM_MAX_KEY_SIZE)) {
330               printf("looking up term from lexicon failed.\n"
331                      " termid: %d\n", tid);
332               f->cerr++;
333             } else {
334               /* FIXME: check lexicon size and print non-null terminated fix-size term */
335               printf("  %10d,%10d,%10d,%10d | %s\n", tid, df, sf, nposts, term);
336             }
337           }
338           tdf += df;
339           tsf += sf;
340           tnposts += nposts;
341           sen_inv_cursor_close(cur);
342         }
343         c++;
344       }
345       printf(" checked inv file %s: total %d entiries\n"
346              " checked: %d records/%d sections/%d posts\n", f->path, c, tdf, tsf, tnposts);
347       e = f->cerr;
348     }
349   } else {
350     /* handle segv */
351     e = -4;
352   }
353 exit:
354   if (i) { sen_index_close(i); }
355   if (keys) { sen_sym_close(keys); }
356   if (lex) { sen_sym_close(lex); }
357   return e;
358 }
359 
360 /* copy from store.c */
361 inline static void
gen_pathname(const char * path,char * buffer,int fno)362 gen_pathname(const char *path, char *buffer, int fno)
363 {
364   size_t len = strlen(path);
365   memcpy(buffer, path, len);
366   if (fno >= 0) {
367     buffer[len] = '.';
368     sen_str_itoh(fno, buffer + len + 1, 7);
369   } else {
370     buffer[len] = '\0';
371   }
372 }
373 
374 char *
cat_pathname(const char * path,const char * ext)375 cat_pathname(const char *path, const char *ext)
376 {
377   char *buf;
378   if (!path || !ext) { return NULL; }
379   {
380     size_t path_len = strlen(path);
381     size_t ext_len = strlen(ext);
382     if (path_len + ext_len >= PATH_MAX) { return NULL; }
383     if ((buf = SEN_GMALLOC(path_len + ext_len + 1))) {
384       memcpy(buf, path, path_len);
385       memcpy(buf + path_len, ext, ext_len + 1);
386     }
387     return buf;
388   }
389 }
390 
391 int
check_file_existence(const char * path,const char * ext)392 check_file_existence(const char *path, const char *ext)
393 {
394   FILE *fp;
395   const char *buf;
396   int ret = 0;
397   if (!path) { return 0; }
398   if (ext) {
399     if (!(buf = cat_pathname(path, ext))) { return 0; }
400   } else {
401     buf = path;
402   }
403   if (!(fp = fopen(buf, "r"))) {
404     switch(errno) {
405       case ENOENT:
406         goto exit;
407       case EACCES:
408         printf("cannot access %s.\n", buf);
409         goto exit;
410       default:
411         printf("error: [%s][%d] %s\n", buf, errno, strerror(errno));
412         goto exit;
413     }
414   }
415   fclose(fp);
416   ret = 1;
417 exit:
418   if (ext) { SEN_GFREE((char *)buf); }
419   return ret;
420 }
421 
422 senchk_filelist *
senchk_filelist_init(unsigned int max_n_files)423 senchk_filelist_init(unsigned int max_n_files)
424 {
425   senchk_filelist *fl;
426   if ((fl = SEN_GMALLOC(sizeof(senchk_filelist)))) {
427     if ((fl->files = SEN_GMALLOC(max_n_files * sizeof(senchk_file)))) {
428       fl->n_files = 0;
429       fl->max_n_files = max_n_files;
430       return fl;
431     }
432     SEN_GFREE(fl);
433   }
434   return NULL;
435 }
436 
437 inline static void
senchk_filelist_expand(senchk_filelist * fl)438 senchk_filelist_expand(senchk_filelist *fl)
439 {
440   senchk_file *files;
441   if (fl->n_files < fl->max_n_files) { return; }
442   fl->max_n_files <<= 1;
443   if (!(files = SEN_GREALLOC(fl->files,
444                              fl->max_n_files * sizeof(senchk_file)))) {
445     puts("memory realloction failed: on senchk_filelist_expand");
446     fl->max_n_files >>= 1;
447     fl->n_files = 0; /* overwrite */
448     return;
449   }
450   fl->files = files;
451 }
452 
453 int
senchk_filelist_add(senchk_filelist * fl,senchk_filetype type,const char * path,const char * keyspath,const char * lexpath)454 senchk_filelist_add(senchk_filelist *fl, senchk_filetype type,
455                     const char *path, const char *keyspath, const char *lexpath)
456 {
457   if (fl && path) {
458     senchk_file *f = fl->files + fl->n_files;
459     f->type = type;
460     if (type == type_inv && keyspath && lexpath) {
461       f->keyspath = strdup(keyspath);
462       f->lexpath = strdup(lexpath);
463     } else {
464       f->keyspath = f->lexpath = NULL;
465     }
466     f->path = strdup(path);
467     f->cerr = 0;
468     fl->n_files++;
469     senchk_filelist_expand(fl);
470     return 1;
471   }
472   return 0;
473 }
474 
475 int
senchk_filelist_addi(senchk_filelist * fl,senchk_filetype type,void * instance)476 senchk_filelist_addi(senchk_filelist *fl, senchk_filetype type, void *instance)
477 {
478   if (fl && instance) {
479     senchk_file *f = fl->files + fl->n_files;
480     f->type = type;
481     f->keyspath = f->lexpath = NULL;
482     switch(type) {
483       case type_sym:
484         if (((sen_sym *)instance)->v08p) {
485           puts("files made by senna version 0.8 is not supported.");
486           return 0;
487         }
488         f->path = strdup(((sen_sym *)instance)->io->path);
489         break;
490       case type_inv:
491         {
492           /* TODO: move sen_inv to inv.h */
493           /* f->path = strdup(((sen_index *)instance)->seg->path) */
494           f->keyspath = strdup(((sen_index *)instance)->keys->io->path);
495           f->lexpath = strdup(((sen_index *)instance)->lexicon->io->path);
496 
497           /* FIXME: temporal implementation */
498           f->path = strdup(f->lexpath);
499           f->path[strlen(f->path) - 1] = 'i';
500         }
501         break;
502       case type_ra:
503         /* TODO: implement */
504         break;
505       case type_ja:
506         /* TODO: implement */
507         break;
508       default:
509         /* TODO: error */
510         break;
511     }
512     f->cerr = 0;
513     fl->n_files++;
514     senchk_filelist_expand(fl);
515     return 1;
516   }
517   return 0;
518 }
519 
520 void
senchk_filelist_fin(senchk_filelist * fl)521 senchk_filelist_fin(senchk_filelist *fl)
522 {
523   senchk_file *f, *end;
524   if (!fl) { return; }
525   end = fl->files + fl->n_files;
526   for (f = fl->files; f < end; f++) {
527     if (f->path) { SEN_GFREE(f->path); }
528     if (f->keyspath) { SEN_GFREE(f->keyspath); }
529     if (f->lexpath) { SEN_GFREE(f->lexpath); }
530   }
531   SEN_GFREE(fl);
532 }
533 
534 int
senchk_filelist_get(senchk_filelist * fl,const char * path)535 senchk_filelist_get(senchk_filelist *fl, const char *path)
536 {
537   /* returns file count added */
538   int ret = 0;
539   if (check_file_existence(path, NULL)) {
540     sen_db *db;
541     if ((db = sen_db_open(path)) && !segvcount) {
542       sen_id cid = 0;
543       if (senchk_filelist_add(fl, type_sym, path, NULL, NULL)) {
544         ret += 1;
545       }
546       while ((cid = sen_sym_next(db->keys, cid)) != SEN_SYM_NIL) {
547         uint32_t spec_len;
548         char buffer[PATH_MAX];
549         sen_db_store_spec *spec;
550         if (!(spec = sen_ja_ref(db->values, cid, &spec_len))) {
551           /* TODO: implement */
552           return 0;
553         }
554         gen_pathname(db->keys->io->path, buffer, cid);
555         switch(spec->type) {
556         case sen_db_raw_class :
557           /* not physical file */
558           break;
559         case sen_db_class :
560           /* sym */
561           /* TODO: implement */
562           if (senchk_filelist_add(fl, type_sym, buffer, NULL, NULL)) {
563             ret += 1;
564           }
565           break;
566         case sen_db_obj_slot :
567           /* TODO: implement obj check */
568           /* ra */
569           if (senchk_filelist_add(fl, type_ra, buffer, NULL, NULL)) {
570             ret += 1;
571           }
572           break;
573         case sen_db_ra_slot :
574           /* ra */
575           if (senchk_filelist_add(fl, type_ra, buffer, NULL, NULL)) {
576             ret += 1;
577           }
578           break;
579         case sen_db_ja_slot :
580           /* ja */
581           if (senchk_filelist_add(fl, type_ja, buffer, NULL, NULL)) {
582             ret += 1;
583           }
584           break;
585         case sen_db_idx_slot :
586           /* inv keys lexicon */
587           {
588             sen_db_store *l, *k;
589             if (!(k = sen_db_store_by_id(db, spec->u.s.class))) {
590               /* TODO: implement */
591             }
592             if (!(l = sen_db_slot_class_by_id(db, cid))) {
593               /* TODO: implement */
594             }
595             if (senchk_filelist_add(fl, type_inv, buffer,
596                                     k->u.c.keys->io->path,
597                                     l->u.c.keys->io->path)) {
598               ret += 1;
599             }
600           }
601           break;
602         default:
603           break;
604         }
605         sen_ja_unref(db->values, cid, spec, spec_len);
606       }
607       sen_db_close(db);
608     }
609   } else if (check_file_existence(path, ".SEN") &&
610              check_file_existence(path, ".SEN.i") &&
611              check_file_existence(path, ".SEN.l")) {
612     sen_index *index;
613     if ((index = sen_index_open(path)) && !segvcount) {
614       /* TODO: check vgram if exists */
615       if (senchk_filelist_addi(fl, type_sym, index->keys) &&
616           senchk_filelist_addi(fl, type_sym, index->lexicon) &&
617           senchk_filelist_addi(fl, type_inv, index)) {
618         ret = 3;
619       }
620       sen_index_close(index);
621     }
622   }
623   return ret;
624 }
625 
626 int
senchk_filelist_check(senchk_filelist * fl)627 senchk_filelist_check(senchk_filelist *fl)
628 {
629   int ngcount = 0;
630   senchk_file *f, *end;
631   if (!fl) { return -1; }
632   end = fl->files + fl->n_files;
633 
634   if (!(chkflags & CHK_UNLOCK)) {
635     /* sym/ra/ja check */
636     for (f = fl->files; f < end; f++) {
637       switch (f->type) {
638         case type_sym:
639           if (check_sym(f)) {
640             ngcount++;
641             printf("* BROKEN SYM FILE: '%s'\n", f->path);
642           }
643           break;
644         case type_ra:
645           /* TODO: implement */
646           show_delimiter(stdout);
647           printf(" check ra file: '%s'\n", f->path);
648           puts("* now checking ra is not implemented...");
649           break;
650         case type_ja:
651           /* TODO: implement */
652           show_delimiter(stdout);
653           printf(" check ja file: '%s'\n", f->path);
654           puts("* now checking ja is not implemented...");
655           break;
656         case type_inv:
657           /* do nothing */
658           break;
659         default:
660           /* TODO: print error */
661           break;
662       }
663     }
664   }
665 
666   /* inv check */
667   for (f = fl->files; f < end; f++) {
668     if (f->type == type_inv) {
669       if (check_inv(f)) {
670         ngcount++;
671         printf("* BROKEN INV FILE: '%s' KEYS: '%s' LEXICON: '%s'\n",
672                f->path, f->keyspath, f->lexpath);
673       }
674     }
675   }
676 
677   return ngcount;
678 }
679 
680 const char *
handleopt(int argc,char ** argv)681 handleopt(int argc, char **argv)
682 {
683   int i;
684   static sen_str_getopt_opt opts[] = {
685     {'l', "lexdump", NULL, CHK_DUMP_LEXICON, getopt_op_on},
686     {'t', "throughsegv", NULL, CHK_THROUGH_SEGV, getopt_op_on},
687     {'u', "unlock", NULL, CHK_UNLOCK, getopt_op_on},
688     {'\0', NULL, NULL, 0, 0}
689   };
690 
691   i = sen_str_getopt(argc, argv, opts, &chkflags);
692   if (i == argc - 1) {
693     return argv[i];
694   } else {
695     return NULL;
696   }
697 }
698 
699 static void
show_seninfo(FILE * fp)700 show_seninfo(FILE *fp)
701 {
702   /* TODO: sen_info revise. confpath will be deprecated. */
703   /* TODO: need sen_enc2str */
704   char *version, *confopt, *confpath;
705   sen_encoding denc;
706   unsigned int nseg, pmt;
707   sen_info(&version, &confopt, &confpath,
708            &denc, &nseg, &pmt);
709   fprintf(fp,
710           "  Senna infomation:\n"
711           "   - Version                 : %s\n"
712           "   - Configure Options       : %s\n"
713           "   - Default Encoding        : %s\n"
714           "   - Initial N-Segments      : %d\n"
715           "   - Partial Match Threshold : %d\n",
716           version, confopt, sen_enctostr(denc), nseg, pmt);
717 }
718 
719 static void
usage(void)720 usage(void)
721 {
722   fprintf(stderr,
723           "Usage: sennachk [options...] senna-file\n"
724           "options:\n"
725           "  --lexdump, -l     : print lex words on checking inv file\n"
726           "  --throughsegv, -t : run without trap SIGSEGV\n"
727           "  --unlock, -u      : unlock index lock\n"
728           "senna-file: <index path> or <db path>\n");
729 }
730 
731 int
main(int argc,char * argv[])732 main(int argc, char *argv[])
733 {
734   int ret, files, ngfiles;
735   const char *path;
736   senchk_filelist *fls;
737 #ifndef WIN32
738   struct sigaction m;
739 #endif /* WIN32 */
740 
741   if (!(path = handleopt(argc, argv))) {
742     usage();
743     return 1;
744   }
745 
746   if (!(chkflags & CHK_THROUGH_SEGV)) {
747 #ifdef WIN32
748     if (signal(SIGSEGV, trapsegv) == SIG_ERR) {
749 #else /* WIN32 */
750     m.sa_flags = SA_SIGINFO;
751     m.sa_sigaction = (void *)trapsegv;
752     if (sigaction(SIGSEGV, &m, NULL) < 0) {
753 #endif /* WIN32 */
754       printf("cannot handle segv");
755       return 2;
756     }
757   }
758 
759   sen_init();
760   show_delimiter(stdout);
761   show_seninfo(stdout);
762 
763   if (!(fls = senchk_filelist_init(INITIAL_CHECK_FILES))) {
764     puts("cannot init filelist object");
765     ret = 3;
766     goto exit;
767   }
768 
769   if (!(files = senchk_filelist_get(fls, path)) || segvcount) {
770     puts("cannot find senna index.");
771     ret = 4;
772     goto exit;
773   }
774 
775   ngfiles = senchk_filelist_check(fls);
776 
777   show_delimiter(stdout);
778   printf(" %d file(s) checked, and %d file(s) are invalid\n", files, ngfiles);
779 exit:
780   if (fls) {
781     senchk_filelist_fin(fls);
782   }
783   sen_fin();
784 
785   return 0;
786 }
787