"Fossies" - the Fresh Open Source Software Archive

Member "mairix-0.24/mairix.c" (13 Aug 2017, 26086 Bytes) of package /linux/privat/mairix-0.24.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mairix.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.23_vs_0.24.

    1 /*
    2   mairix - message index builder and finder for maildir folders.
    3 
    4  **********************************************************************
    5  * Copyright (C) Richard P. Curnow  2002,2003,2004,2005,2006,2007,2008
    6  * Copyright (C) Sanjoy Mahajan 2005
    7  * - mfolder validation code
    8  * Copyright (C) James Cameron 2005
    9  * Copyright (C) Paul Fox 2006
   10  *
   11  * This program is free software; you can redistribute it and/or modify
   12  * it under the terms of version 2 of the GNU General Public License as
   13  * published by the Free Software Foundation.
   14  *
   15  * This program is distributed in the hope that it will be useful, but
   16  * WITHOUT ANY WARRANTY; without even the implied warranty of
   17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   18  * General Public License for more details.
   19  *
   20  * You should have received a copy of the GNU General Public License along
   21  * with this program; if not, write to the Free Software Foundation, Inc.,
   22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   23  *
   24  **********************************************************************
   25  */
   26 
   27 #include "mairix.h"
   28 #include "version.h"
   29 #include <assert.h>
   30 #include <sys/types.h>
   31 #include <sys/stat.h>
   32 #include <pwd.h>
   33 #include <unistd.h>
   34 #include <ctype.h>
   35 #include <locale.h>
   36 #include <signal.h>
   37 #include "imapinterface.h"
   38 
   39 #ifdef TEST_OOM
   40 int total_bytes=0;
   41 #endif
   42 
   43 int verbose = 0;
   44 int do_hardlinks = 0;
   45 
   46 static char *folder_base = NULL;
   47 static char *maildir_folders = NULL;
   48 static char *mh_folders = NULL;
   49 static char *mboxen = NULL;
   50 static char *imap_folders = NULL;
   51 static char *imap_pipe = NULL;
   52 static char *imap_server = NULL;
   53 static char *imap_username = NULL;
   54 static char *imap_password = NULL;
   55 static char *mfolder = NULL;
   56 static char *omit = NULL;
   57 static char *database_path = NULL;
   58 static enum folder_type output_folder_type = FT_MAILDIR;
   59 static int skip_integrity_checks = 0;
   60 static int follow_mbox_symlinks = 0;
   61 
   62 enum filetype {
   63   M_NONE, M_FILE, M_DIR, M_OTHER
   64 };
   65 
   66 static enum filetype classify_file(char *name)/*{{{*/
   67 {
   68   struct stat sb;
   69   if (stat(name, &sb) < 0) {
   70     return M_NONE;
   71   }
   72   if (S_ISREG(sb.st_mode)) {
   73     return M_FILE;
   74   } else if (S_ISDIR(sb.st_mode)) {
   75     return M_DIR;
   76   } else {
   77     return M_OTHER;
   78   }
   79 }
   80 /*}}}*/
   81 /*{{{ member of*/
   82 /* returns 1 iff COMPLETE_MFOLDER (i.e. the match folder with
   83    folder_base prepended if needed) matches one of the FOLDERS after
   84    expanding the wildcards and recursion. Used to make sure that the
   85    match folder will not overwrite a valuable mail file or
   86    directory.  */
   87 int member_of (const char *complete_mfolder,
   88     const char *folder_base,
   89     const char *folders,
   90     enum folder_type ft,
   91     struct globber_array *omit_globs) {
   92   char **raw_paths, **paths;
   93   int n_raw_paths, n_paths, i;
   94 
   95   if (!folders)
   96     return 0;
   97   split_on_colons(folders, &n_raw_paths, &raw_paths);
   98   switch (ft) {
   99     case FT_MAILDIR:
  100       glob_and_expand_paths(folder_base, raw_paths, n_raw_paths, &paths, &n_paths, &maildir_traverse_methods, omit_globs);
  101       break;
  102     case FT_MH:
  103       glob_and_expand_paths(folder_base, raw_paths, n_raw_paths, &paths, &n_paths, &mh_traverse_methods, omit_globs);
  104       break;
  105     case FT_MBOX:
  106       glob_and_expand_paths(folder_base, raw_paths, n_raw_paths, &paths, &n_paths, &mbox_traverse_methods, omit_globs);
  107       break;
  108     case FT_RAW: /* cannot happen but to keep compiler happy */
  109     case FT_EXCERPT:
  110     case FT_IMAP:
  111       break;
  112   }
  113   for (i=0; i<n_paths; i++) {
  114     struct stat mfolder_sb, src_folder_sb; /* for checking inode numbers */
  115 
  116     /* if the complete path names are the same, definitely a match */
  117     if (strcmp (complete_mfolder, paths[i]) == 0)
  118       return 1;
  119     /* also a match if they point to the same file or directory but
  120        via different routes (e.g. absolute path for one but path with
  121        ../.. for the other), so check inode numbers */
  122     /* if cannot even get stat() info, probably not wrecking any mail
  123        files or dirs, so continue, i.e. skip inode check. */
  124     if (stat (complete_mfolder, &mfolder_sb) != 0 ||
  125         stat (paths[i], &src_folder_sb) != 0)
  126       continue;
  127     if (mfolder_sb.st_ino == src_folder_sb.st_ino)
  128       return 1;
  129   }
  130   return 0;
  131 }
  132 /*}}}*/
  133 static char *copy_value(char *text)/*{{{*/
  134 {
  135   char *p;
  136   char *result;
  137   for (p = text; *p && (*p != '='); p++) ;
  138   if (!*p) return NULL;
  139   p++;
  140   result = expand_string(p);
  141   return result;
  142 }
  143 /*}}}*/
  144 static void add_folders(char **folders, char *extra_folders)/*{{{*/
  145 {
  146   /* note : extra_pointers is stale after this routine exits. */
  147 
  148   if (!*folders) {
  149     *folders = extra_folders;
  150   } else {
  151     char *old_folders = *folders;
  152     char *new_folders;
  153     int old_len, extra_len;
  154     old_len = strlen(old_folders);
  155     extra_len = strlen(extra_folders);
  156     new_folders = new_array(char, old_len + extra_len + 2);
  157     strcpy(new_folders, old_folders);
  158     strcpy(new_folders + old_len, ":");
  159     strcpy(new_folders + old_len + 1, extra_folders);
  160     *folders = new_folders;
  161     free(old_folders);
  162   }
  163 }
  164 /*}}}*/
  165 static void parse_output_folder(char *p)/*{{{*/
  166 {
  167   char *temp;
  168   temp = copy_value(p);
  169   if (!strncasecmp(temp, "mh", 2)) {
  170     output_folder_type = FT_MH;
  171   } else if (!strncasecmp(temp, "maildir", 7)) {
  172     output_folder_type = FT_MAILDIR;
  173   } else if (!strncasecmp(temp, "raw", 3)) {
  174     output_folder_type = FT_RAW;
  175   } else if (!strncasecmp(temp, "excerpt", 3)) {
  176     output_folder_type = FT_EXCERPT;
  177   } else if (!strncasecmp(temp, "mbox", 4)) {
  178     output_folder_type = FT_MBOX;
  179   } else if (!strncasecmp(temp, "imap", 4)) {
  180     output_folder_type = FT_IMAP;
  181   }
  182   else {
  183     fprintf(stderr, "Unrecognized mformat <%s>\n", temp);
  184   }
  185   free(temp);
  186 }
  187 /*}}}*/
  188 static void parse_rc_file(char *name)/*{{{*/
  189 {
  190   FILE *in;
  191   char line[4096], *p;
  192   int len, lineno;
  193   int all_blank;
  194   int used_default_name = 0;
  195 
  196   if (!name) {
  197     /* open default file */
  198     struct passwd *pw;
  199     char *home;
  200     home = getenv("HOME");
  201     if (!home) {
  202       pw = getpwuid(getuid());
  203       if (!pw) {
  204         fprintf(stderr, "Cannot determine home directory\n");
  205         exit(2);
  206       }
  207       home = pw->pw_dir;
  208     }
  209     name = new_array(char, strlen(home) + 12);
  210     strcpy(name, home);
  211     strcat(name, "/.mairixrc");
  212     used_default_name = 1;
  213   }
  214 
  215   in = fopen(name, "r");
  216   if (!in) {
  217     fprintf(stderr, "Cannot open %s, exiting\n", name);
  218     exit(2);
  219   }
  220 
  221   lineno = 0;
  222   while(fgets(line, sizeof(line), in)) {
  223     lineno++;
  224     len = strlen(line);
  225     if (len > sizeof(line) - 4) {
  226       fprintf(stderr, "Line %d in %s too long, exiting\n", lineno, name);
  227       exit(2);
  228     }
  229 
  230     if (line[len-1] == '\n') {
  231       line[len-1] = '\0';
  232     }
  233 
  234     /* Strip trailing comments. */
  235     for (p=line; *p && !strchr("#!;%", *p); p++) ;
  236     if (*p) *p = '\0';
  237 
  238     /* Discard blank lines */
  239     all_blank = 1;
  240     for (p=line; *p; p++) {
  241       if (!isspace(*(unsigned char *)p)) {
  242         all_blank = 0;
  243         break;
  244       }
  245     }
  246 
  247     if (all_blank) continue;
  248 
  249     /* Now a real line to parse */
  250     if (!strncasecmp(p, "base", 4)) folder_base = copy_value(p);
  251     else if (!strncasecmp(p, "folders", 7)) {
  252       fprintf(stderr, "'folders=' option in rc file is depracated, use 'maildir='\n");
  253       add_folders(&maildir_folders, copy_value(p));
  254     }
  255     else if (!strncasecmp(p, "maildir=", 8)) add_folders(&maildir_folders, copy_value(p));
  256     else if (!strncasecmp(p, "mh_folders=", 11)) {
  257       fprintf(stderr, "'mh_folders=' option in rc file is depracated, use 'mh='\n");
  258       add_folders(&mh_folders, copy_value(p));
  259     }
  260     else if (!strncasecmp(p, "mh=", 3)) add_folders(&mh_folders, copy_value(p));
  261     else if (!strncasecmp(p, "mbox=", 5)) add_folders(&mboxen, copy_value(p));
  262     else if (!strncasecmp(p, "imap=", 5)) add_folders(&imap_folders, copy_value(p));
  263     else if (!strncasecmp(p, "imap_pipe=", 10)) imap_pipe = copy_value(p);
  264     else if (!strncasecmp(p, "imap_server=", 12)) imap_server = copy_value(p);
  265     else if (!strncasecmp(p, "imap_username=", 14)) imap_username = copy_value(p);
  266     else if (!strncasecmp(p, "imap_password=", 14)) imap_password = copy_value(p);
  267     else if (!strncasecmp(p, "follow_mbox_symlinks", 20)) follow_mbox_symlinks = 1;
  268     else if (!strncasecmp(p, "omit=", 5)) add_folders(&omit, copy_value(p));
  269 
  270     else if (!strncasecmp(p, "mformat=", 8)) {
  271       parse_output_folder(p);
  272     }
  273     else if (!strncasecmp(p, "mfolder=", 8)) mfolder = copy_value(p);
  274     else if (!strncasecmp(p, "database=", 9)) database_path = copy_value(p);
  275     else if (!strncasecmp(p, "nochecks", 8)) skip_integrity_checks = 1;
  276     else {
  277       if (verbose) {
  278         fprintf(stderr, "Unrecognized option at line %d in %s\n", lineno, name);
  279       }
  280     }
  281   }
  282 
  283   fclose(in);
  284 
  285   if (used_default_name) free(name);
  286 }
  287 /*}}}*/
  288 
  289 static int message_compare(const void *a, const void *b)/*{{{*/
  290 {
  291   /* FIXME : Is this a sensible way to do this with mbox messages in the picture? */
  292   struct msgpath *aa = (struct msgpath *) a;
  293   struct msgpath *bb = (struct msgpath *) b;
  294   if (aa->type < bb->type) return -1;
  295   if (aa->type > bb->type) return 1;
  296   return strcmp(aa->src.mpf.path, bb->src.mpf.path);
  297 }
  298 /*}}}*/
  299 static void sort_message_list(struct msgpath_array *arr)/*{{{*/
  300 {
  301   qsort(arr->paths, arr->n, sizeof(struct msgpath), message_compare);
  302 }
  303 /*}}}*/
  304 
  305 static int compare_strings(const void *a, const void *b)/*{{{*/
  306 {
  307   const char **aa = (const char **) a;
  308   const char **bb = (const char **) b;
  309   return strcmp(*aa, *bb);
  310 }
  311 /*}}}*/
  312 static int check_message_list_for_duplicates(struct msgpath_array *msgs)/*{{{*/
  313 {
  314   /* Caveat : only examines the file-per-message case */
  315   char **sorted_paths, **sorted_imap;
  316   int i, n, nn, imap_nn;
  317   int result;
  318 
  319   n = msgs->n;
  320   sorted_paths = new_array(char *, n);
  321   sorted_imap = new_array(char *, n);
  322   for (i=0, nn=0, imap_nn=0; i<n; i++) {
  323     switch (msgs->paths[i].type) {
  324       case MTY_MBOX:
  325         break;
  326       case MTY_DEAD:
  327         assert(0);
  328         break;
  329       case MTY_FILE:
  330         sorted_paths[nn++] = msgs->paths[i].src.mpf.path;
  331         break;
  332       case MTY_IMAP:
  333         sorted_imap[imap_nn++] = msgs->paths[i].src.mpf.path;
  334         break;
  335     }
  336   }
  337   qsort(sorted_paths, nn, sizeof(char *), compare_strings);
  338   qsort(sorted_imap, imap_nn, sizeof(char *), compare_strings);
  339 
  340   result = 0;
  341   for (i=1; i<nn; i++) {
  342     if (!strcmp(sorted_paths[i-1], sorted_paths[i])) {
  343       result = 1;
  344       break;
  345     }
  346   }
  347   for (i=1; i<imap_nn; i++) {
  348     if (!strcmp(sorted_imap[i-1], sorted_imap[i])) {
  349       result = 1;
  350       break;
  351     }
  352   }
  353 
  354   free(sorted_paths);
  355   free(sorted_imap);
  356   return result;
  357 }
  358 /*}}}*/
  359 
  360 static void emit_int(int x)/*{{{*/
  361 {
  362   char buf1[20], buf2[20];
  363   char *p, *q;
  364   int neg=0;
  365   p = buf1;
  366   *p = '0'; /* In case x is zero */
  367   if (x < 0) {
  368     neg = 1;
  369     x = -x;
  370   }
  371   while (x) {
  372     *p++ = '0' + (x % 10);
  373     x /= 10;
  374   }
  375   p--;
  376   q = buf2;
  377   if (neg) *q++ = '-';
  378   while (p >= buf1) {
  379     *q++ = *p--;
  380   }
  381   write(2, buf2, q-buf2);
  382   return;
  383 }
  384 /*}}}*/
  385 void out_of_mem(char *file, int line, size_t size)/*{{{*/
  386 {
  387   /* Hairy coding ahead - can't use any [s]printf, itoa etc because
  388    * those might try to use the heap! */
  389 
  390   int filelen;
  391   char *p;
  392 
  393   static char msg1[] = "Out of memory (at ";
  394   static char msg2[] = " bytes)\n";
  395   /* Perhaps even strlen is unsafe in this situation? */
  396   p = file;
  397   while (*p) p++;
  398   filelen = p - file;
  399   write(2, msg1, sizeof(msg1)-1);
  400   write(2, file, filelen);
  401   write(2, ":", 1);
  402   emit_int(line);
  403   write(2, ", ", 2);
  404   emit_int(size);
  405   write(2, msg2, sizeof(msg2)-1);
  406   exit(2);
  407 }
  408 /*}}}*/
  409 void report_error(const char *str, const char *filename)/*{{{*/
  410 {
  411   if (filename) {
  412     int len = strlen(str) + strlen(filename) + 4;
  413     char *t;
  414     t = new_array(char, len);
  415     sprintf(t, "%s '%s'", str, filename);
  416     perror(t);
  417     free(t);
  418   } else {
  419     perror(str);
  420   }
  421 }
  422 /*}}}*/
  423 static void print_copyright(void)/*{{{*/
  424 {
  425   fprintf(stderr,
  426           "mairix %s, Copyright (C) 2002-2010 Richard P. Curnow\n"
  427           "mairix comes with ABSOLUTELY NO WARRANTY.\n"
  428           "This is free software, and you are welcome to redistribute it\n"
  429           "under certain conditions; see the GNU General Public License for details.\n\n",
  430           PROGRAM_VERSION);
  431 }
  432 /*}}}*/
  433 static void print_version(void)/*{{{*/
  434 {
  435   fprintf(stdout,
  436           "mairix %s\n",
  437           PROGRAM_VERSION);
  438 }
  439 /*}}}*/
  440 static void handlesig(int signo)/*{{{*/
  441 {
  442   unlock_and_exit(7);
  443 }
  444 /*}}}*/
  445 static void usage(void)/*{{{*/
  446 {
  447   print_copyright();
  448 
  449   printf("mairix [-h]                                    : Show help\n"
  450          "mairix [-f <rcfile>] [-v] [-p] [-F]            : Build index\n"
  451          "mairix [-f <rcfile>] [-a] [-t] expr1 ... exprN : Run search\n"
  452          "mairix [-f <rcfile>] -d                        : Dump database to stdout\n"
  453          "-h           : show this help\n"
  454          "-f <rcfile>  : use alternative rc file (default ~/.mairixrc)\n"
  455          "-V           : show version\n"
  456          "-v           : be verbose\n"
  457          "-p           : purge messages that no longer exist\n"
  458          "-F           : fast scan for maildir and MH folders (no mtime or size checks)\n"
  459          "-a           : add new matches to match folder (default : clear it first)\n"
  460          "-x           : display excerpt of message headers (default : use match folder)\n" 
  461          "-t           : include all messages in same threads as matching messages\n"
  462          "-o <mfolder> : override setting of mfolder from mairixrc file\n"
  463          "-r           : force raw output regardless of mformat setting in mairixrc file\n"
  464          "-H           : force hard links rather than symbolic ones\n"
  465          "expr_i       : search expression (all expr's AND'ed together):\n"
  466          "    word          : match word in message body and major headers\n"
  467          "    t:word        : match word in To: header\n"
  468          "    c:word        : match word in Cc: header\n"
  469          "    f:word        : match word in From: header\n"
  470          "    a:word        : match word in To:, Cc: or From: headers (address)\n"
  471          "    s:word        : match word in Subject: header\n"
  472          "    b:word        : match word in message body\n"
  473          "    m:word        : match word in Message-ID: header\n"
  474          "    n:word        : match name of attachment within message\n"
  475          "    F:flags       : match on message flags (s=seen,r=replied,f=flagged,-=negate)\n"
  476          "    p:substring   : match substring of path\n"
  477          "    d:start-end   : match date range\n"
  478          "    z:low-high    : match messages in size range\n"
  479          "    bs:word       : match word in Subject: header or body (or any other group of prefixes)\n"
  480          "    s:word1,word2 : match both words in Subject:\n"
  481          "    s:word1/word2 : match either word or both words in Subject:\n"
  482          "    s:~word       : match messages not containing word in Subject:\n"
  483          "    s:substring=  : match substring in any word in Subject:\n"
  484          "    s:^substring= : match left-anchored substring in any word in Subject:\n"
  485          "    s:substring=2 : match substring with <=2 errors in any word in Subject:\n"
  486          "\n"
  487          "    (See documentation for more examples)\n"
  488          );
  489 }
  490     /*}}}*/
  491 /* Notes on folder management: {{{
  492 
  493    Assumption is that the user wants to keep the 'mfolder' directories under a
  494    common root with the real maildir folders.  This allows a common value for
  495    mutt's 'folder' variable => the '+' and '=' prefixes work better.  This
  496    means the indexer here can't just scan down all subdirectories of a single
  497    ancestor, because it'll pick up its own mfolders.  So, use environment
  498    variables to tailor the folders.
  499 
  500    MAIRIX_FOLDER_BASE is the common parent directory of the folders (aka
  501    mutt's 'folder' variable)
  502 
  503    MAIRIX_MAILDIR_FOLDERS, MAIRIX_MH_FOLDERS, MAIRIX_MBOXEN are
  504    colon-separated lists of folders to index, with '...' after a
  505    component meaning any maildir underneath it.
  506 
  507    MAIRIX_MFOLDER is the folder to put the match data.
  508 
  509    For example, if
  510    MAIRIX_FOLDER_BASE = "/home/foobar/mail"
  511    MAIRIX_FOLDERS = "inbox:lists...:action:archive..."
  512    MAIRIX_MFOLDER = "mf"
  513 
  514    then /home/foobar/mail/mf/{new,cur,tmp} contain the output of the search.
  515    }}} */
  516 
  517 int main (int argc, char **argv)/*{{{*/
  518 {
  519   struct msgpath_array *msgs;
  520   struct database *db = NULL;
  521 
  522   char *arg_rc_file_path = NULL;
  523   char *arg_mfolder = NULL;
  524   char *e;
  525   int do_augment = 0;
  526   int do_threads = 0;
  527   int do_search = 0;
  528   int do_purge = 0;
  529   int any_updates = 0;
  530   int any_purges = 0;
  531   int do_help = 0;
  532   int do_raw_output = 0;
  533   int do_excerpt_output = 0;
  534   int do_dump = 0;
  535   int do_integrity_checks = 1;
  536   int do_forced_unlock = 0;
  537   int do_fast_index = 0;
  538   int do_mbox_symlinks = 0;
  539   struct imap_ll *imapc = NULL;
  540 
  541   unsigned int forced_hash_key = CREATE_RANDOM_DATABASE_HASH;
  542 
  543   struct globber_array *omit_globs;
  544 
  545   int result;
  546 
  547   setlocale(LC_CTYPE, "");
  548 
  549   while (++argv, --argc) {
  550     if (!*argv) {
  551       break;
  552     } else if (!strcmp(*argv, "-f") || !strcmp(*argv, "--rcfile")) {
  553       ++argv, --argc;
  554       if (!argc) {
  555         fprintf(stderr, "No filename given after -f argument\n");
  556         exit(1);
  557       }
  558       arg_rc_file_path = *argv;
  559     } else if (!strcmp(*argv, "-t") || !strcmp(*argv, "--threads")) {
  560       do_search = 1;
  561       do_threads = 1;
  562     } else if (!strcmp(*argv, "-a") || !strcmp(*argv, "--augment")) {
  563       do_search = 1;
  564       do_augment = 1;
  565     } else if (!strcmp(*argv, "-o") || !strcmp(*argv, "--mfolder")) {
  566       ++argv, --argc;
  567       if (!argc) {
  568         fprintf(stderr, "No folder name given after -o argument\n");
  569         exit(1);
  570       }
  571       arg_mfolder = *argv;
  572     } else if (!strcmp(*argv, "-p") || !strcmp(*argv, "--purge")) {
  573       do_purge = 1;
  574     } else if (!strcmp(*argv, "-d") || !strcmp(*argv, "--dump")) {
  575       do_dump = 1;
  576     } else if (!strcmp(*argv, "-r") || !strcmp(*argv, "--raw-output")) {
  577       do_raw_output = 1;
  578     } else if (!strcmp(*argv, "-x") || !strcmp(*argv, "--excerpt-output")) {
  579       do_excerpt_output = 1;
  580     } else if (!strcmp(*argv, "-H") || !strcmp(*argv, "--force-hardlinks")) {
  581       do_hardlinks = 1;
  582     } else if (!strcmp(*argv, "-Q") || !strcmp(*argv, "--no-integrity-checks")) {
  583       do_integrity_checks = 0;
  584     } else if (!strcmp(*argv, "--unlock")) {
  585       do_forced_unlock = 1;
  586     } else if (!strcmp(*argv, "-F") ||
  587                !strcmp(*argv, "--fast-index")) {
  588       do_fast_index = 1;
  589     } else if (!strcmp(*argv, "--force-hash-key-new-database")) {
  590       ++argv, --argc;
  591       if (!argc) {
  592         fprintf(stderr, "No hash key given after --force-hash-key-new-database\n");
  593         exit(1);
  594       }
  595       if ( 1 != sscanf(*argv, "%u", &forced_hash_key) )
  596     {
  597         fprintf(stderr, "Hash key given after --force-hash-key-new-database could not be parsed\n");
  598         exit(1);
  599     }
  600     } else if (!strcmp(*argv, "-v") || !strcmp(*argv, "--verbose")) {
  601       verbose = 1;
  602     } else if (!strcmp(*argv, "-V") || !strcmp(*argv, "--version")) {
  603       print_version();
  604       exit(0);
  605     } else if (!strcmp(*argv, "-h") ||
  606                !strcmp(*argv, "--help")) {
  607       do_help = 1;
  608     } else if (!strcmp(*argv, "--")) {
  609       /* End of args */
  610       argc--;
  611       argv++;
  612       break;
  613     } else if ((*argv)[0] == '-') {
  614       fprintf(stderr, "Unrecognized option %s\n", *argv);
  615       exit(3);
  616     } else {
  617       /* standard args start */
  618       break;
  619     }
  620   }
  621 
  622   if (do_help) {
  623     usage();
  624     exit(0);
  625   }
  626 
  627   if (verbose) {
  628     print_copyright();
  629   }
  630 
  631   if (*argv) {
  632     /* There are still args to process */
  633     do_search = 1;
  634   }
  635 
  636   parse_rc_file(arg_rc_file_path);
  637 
  638   if (getenv("MAIRIX_FOLDER_BASE")) {
  639     folder_base = getenv("MAIRIX_FOLDER_BASE");
  640   }
  641 
  642   if (getenv("MAIRIX_MAILDIR_FOLDERS")) {
  643     maildir_folders = getenv("MAIRIX_MAIDIR_FOLDERS");
  644   }
  645 
  646   if (getenv("MAIRIX_MH_FOLDERS")) {
  647     mh_folders = getenv("MAIRIX_MH_FOLDERS");
  648   }
  649 
  650   if ((e = getenv("MAIRIX_MBOXEN"))) {
  651     mboxen = e;
  652   }
  653 
  654   if (getenv("MAIRIX_MFOLDER")) {
  655     mfolder = getenv("MAIRIX_MFOLDER");
  656   }
  657 
  658   if (getenv("MAIRIX_DATABASE")) {
  659     database_path = getenv("MAIRIX_DATABASE");
  660   }
  661 
  662   if (arg_mfolder) {
  663     mfolder = arg_mfolder;
  664   }
  665 
  666   if (skip_integrity_checks) {
  667     do_integrity_checks = 0;
  668   }
  669 
  670   if (follow_mbox_symlinks) {
  671     do_mbox_symlinks = 1;
  672   }
  673 
  674   if (!folder_base) {
  675     fprintf(stderr, "No folder_base/MAIRIX_FOLDER_BASE set\n");
  676     exit(2);
  677   }
  678 
  679   if (!database_path) {
  680     fprintf(stderr, "No database/MAIRIX_DATABASE set\n");
  681     exit(2);
  682   }
  683 
  684   if (do_raw_output) {
  685     output_folder_type = FT_RAW;
  686   } else if (do_excerpt_output) {
  687     output_folder_type = FT_EXCERPT;
  688   }
  689 
  690   if (omit) {
  691     omit_globs = colon_sep_string_to_globber_array(omit);
  692   } else {
  693     omit_globs = NULL;
  694   }
  695 
  696   /* Lock database.
  697    * Prevent concurrent updates due to parallel indexing (e.g. due to stuck
  698    * cron jobs).
  699    * Prevent concurrent searching and indexing. */
  700 
  701   signal(SIGHUP, handlesig);
  702   signal(SIGINT, handlesig);
  703   signal(SIGQUIT, handlesig);
  704 
  705   lock_database(database_path, do_forced_unlock);
  706 
  707   if (do_dump) {
  708     dump_database(database_path);
  709     result = 0;
  710 
  711   } else if (do_search) {
  712     int len;
  713     char *complete_mfolder;
  714     enum filetype ftype;
  715 
  716     if (!mfolder) {
  717       switch (output_folder_type) {
  718         case FT_RAW:
  719         case FT_EXCERPT:
  720           break;
  721         default:
  722           fprintf(stderr, "No mfolder/MAIRIX_MFOLDER set\n");
  723           unlock_and_exit(2);
  724       }
  725       mfolder = new_string("");
  726     }
  727 
  728     if (output_folder_type == FT_IMAP) {
  729       complete_mfolder = new_string(mfolder);
  730     } else {
  731       /* complete_mfolder is needed by search_top() and member_of() so
  732          compute it once here rather than in search_top() as well */
  733       if ((mfolder[0] == '/') ||
  734           ((mfolder[0] == '.') && (mfolder[1] == '/'))) {
  735         complete_mfolder = new_string(mfolder);
  736       } else {
  737         len = strlen(folder_base) + strlen(mfolder) + 2;
  738         complete_mfolder = new_array(char, len);
  739         strcpy(complete_mfolder, folder_base);
  740         strcat(complete_mfolder, "/");
  741         strcat(complete_mfolder, mfolder);
  742       }
  743     }
  744     /* check whether mfolder output would destroy a mail folder or mbox */
  745     switch (output_folder_type) {
  746       case FT_RAW:
  747       case FT_EXCERPT:
  748         break;
  749       case FT_IMAP:
  750         /* the same check as below could be implemented in the future */
  751         break;
  752       default:
  753         if ((member_of(complete_mfolder,folder_base, maildir_folders, FT_MAILDIR, omit_globs)||
  754              member_of (complete_mfolder, folder_base, mh_folders, FT_MH, omit_globs) ||
  755              member_of (complete_mfolder, folder_base, mboxen, FT_MBOX, omit_globs))) {
  756           fprintf (stderr,
  757               "You asked search results to go to the folder '%s'.\n"
  758               "That folder appears to be one of the indexed mail folders!\n"
  759               "For your own good, I refuse to output search results to an indexed mail folder.\n",
  760               mfolder);
  761           unlock_and_exit(3);
  762         }
  763     }
  764 
  765     ftype = classify_file(database_path);
  766     if (ftype != M_FILE) {
  767       fprintf(stderr, "No database file '%s' is present.\nYou need to do an indexing run first.\n",
  768           database_path);
  769       unlock_and_exit(3);
  770     }
  771     result = search_top(do_threads, do_augment, database_path, complete_mfolder, argv, output_folder_type, verbose, imap_pipe, imap_server, imap_username, imap_password);
  772 
  773   } else {
  774     enum filetype ftype;
  775 
  776     if (imap_pipe && imap_server) {
  777       fprintf(stderr, "specify one of imap_pipe or imap_server, not both\n");
  778       unlock_and_exit(2);
  779     }
  780 
  781     if (imap_folders && (!(imap_pipe || imap_server))) {
  782       fprintf(stderr, "If imap is given, imap_pipe OR imap_server is required\n");
  783       imap_folders = NULL;
  784     }
  785 
  786     if (!maildir_folders && !mh_folders && !mboxen && !imap_folders) {
  787       fprintf(stderr, "No [mh_]folders/mboxen/imap/MAIRIX_[MH_]FOLDERS set\n");
  788       unlock_and_exit(2);
  789     }
  790 
  791     if (verbose) printf("Finding all currently existing messages...\n");
  792     msgs = new_msgpath_array();
  793     if (imap_folders) {
  794       imapc = imap_start(imap_pipe, imap_server, imap_username, imap_password);
  795       if (!imapc) unlock_and_exit(2);
  796       build_imap_message_list(imap_folders, msgs, omit_globs, imapc);
  797     }
  798     if (maildir_folders) {
  799       build_message_list(folder_base, maildir_folders, FT_MAILDIR, msgs, omit_globs);
  800     }
  801     if (mh_folders) {
  802       build_message_list(folder_base, mh_folders, FT_MH, msgs, omit_globs);
  803     }
  804     sort_message_list(msgs);
  805 
  806     /* The next call sorts the msgs array as part of looking for duplicates. */
  807     if (check_message_list_for_duplicates(msgs)) {
  808       fprintf(stderr, "Message list contains duplicates - check your 'folders' setting\n");
  809       unlock_and_exit(2);
  810     }
  811 
  812     /* Try to open existing database */
  813     ftype = classify_file(database_path);
  814     if (ftype == M_FILE) {
  815       if (verbose) printf("Reading existing database...\n");
  816       db = new_database_from_file(database_path, do_integrity_checks);
  817       if (verbose) printf("Loaded %d existing messages\n", db->n_msgs);
  818     } else if (ftype == M_NONE) {
  819       if (verbose) printf("Starting new database\n");
  820       db = new_database( forced_hash_key );
  821     } else {
  822       fprintf(stderr, "database path %s is not a file; you can't put the database there\n", database_path);
  823       unlock_and_exit(2);
  824     }
  825 
  826     build_mbox_lists(db, folder_base, mboxen, omit_globs, do_mbox_symlinks);
  827 
  828     any_updates = update_database(db, msgs->paths, msgs->n, do_fast_index, imapc);
  829     if (do_purge) {
  830       any_purges = cull_dead_messages(db, do_integrity_checks);
  831     }
  832     if (any_updates || any_purges) {
  833       /* For now write it every time.  This is obviously the most reliable method. */
  834       write_database(db, database_path, do_integrity_checks);
  835     }
  836 
  837 #if 0
  838     get_db_stats(db);
  839 #endif
  840 
  841     free_database(db);
  842     free_msgpath_array(msgs);
  843 
  844     result = 0;
  845   }
  846 
  847   unlock_database();
  848 
  849   return result;
  850 }
  851 /*}}}*/