int main( int argc, char **argv ) { main_vars_t mvars[1]; group_conf_t *group; char *config = 0, *opt, *ochar; int cops = 0, op, pseudo = 0; gethostname( Hostname, sizeof(Hostname) ); if ((ochar = strchr( Hostname, '.' ))) *ochar = 0; Pid = getpid(); if (!(Home = getenv("HOME"))) { fputs( "Fatal: $HOME not set\n", stderr ); return 1; } Ontty = isatty( 1 ) && isatty( 2 ); arc4_init(); memset( mvars, 0, sizeof(*mvars) ); mvars->t[1] = 1; for (mvars->oind = 1, ochar = 0; ; ) { if (!ochar || !*ochar) { if (mvars->oind >= argc) break; if (argv[mvars->oind][0] != '-') break; if (argv[mvars->oind][1] == '-') { opt = argv[mvars->oind++] + 2; if (!*opt) break; if (!strcmp( opt, "config" )) { if (mvars->oind >= argc) { error( "--config requires an argument.\n" ); return 1; } config = argv[mvars->oind++]; } else if (!memcmp( opt, "config=", 7 )) config = opt + 7; else if (!strcmp( opt, "all" )) mvars->all = 1; else if (!strcmp( opt, "list" )) mvars->list = 1; else if (!strcmp( opt, "help" )) usage( 0 ); else if (!strcmp( opt, "version" )) version(); else if (!strcmp( opt, "quiet" )) { if (DFlags & QUIET) DFlags |= VERYQUIET; else DFlags |= QUIET; } else if (!strcmp( opt, "verbose" )) { if (DFlags & VERBOSE) DFlags |= XVERBOSE; else DFlags |= VERBOSE | QUIET; } else if (!strcmp( opt, "debug" )) DFlags |= DEBUG | QUIET; else if (!strcmp( opt, "pull" )) cops |= XOP_PULL, mvars->ops[M] |= XOP_HAVE_TYPE; else if (!strcmp( opt, "push" )) cops |= XOP_PUSH, mvars->ops[M] |= XOP_HAVE_TYPE; else if (!memcmp( opt, "create", 6 )) { opt += 6; op = OP_CREATE|XOP_HAVE_CREATE; lcop: if (!*opt) cops |= op; else if (!strcmp( opt, "-master" )) mvars->ops[M] |= op, ochar++; else if (!strcmp( opt, "-slave" )) mvars->ops[S] |= op, ochar++; else goto badopt; mvars->ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_EXPUNGE); } else if (!memcmp( opt, "expunge", 7 )) { opt += 7; op = OP_EXPUNGE|XOP_HAVE_EXPUNGE; goto lcop; } else if (!strcmp( opt, "no-expunge" )) mvars->ops[M] |= XOP_HAVE_EXPUNGE; else if (!strcmp( opt, "no-create" )) mvars->ops[M] |= XOP_HAVE_CREATE; else if (!strcmp( opt, "full" )) mvars->ops[M] |= XOP_HAVE_TYPE|XOP_PULL|XOP_PUSH; else if (!strcmp( opt, "noop" )) mvars->ops[M] |= XOP_HAVE_TYPE; else if (!memcmp( opt, "pull", 4 )) { op = XOP_PULL; lcac: opt += 4; if (!*opt) cops |= op; else if (*opt == '-') { opt++; goto rlcac; } else goto badopt; } else if (!memcmp( opt, "push", 4 )) { op = XOP_PUSH; goto lcac; } else { op = 0; rlcac: if (!strcmp( opt, "new" )) op |= OP_NEW; else if (!strcmp( opt, "renew" )) op |= OP_RENEW; else if (!strcmp( opt, "delete" )) op |= OP_DELETE; else if (!strcmp( opt, "flags" )) op |= OP_FLAGS; else { badopt: error( "Unknown option '%s'\n", argv[mvars->oind - 1] ); return 1; } switch (op & XOP_MASK_DIR) { case XOP_PULL: mvars->ops[S] |= op & OP_MASK_TYPE; break; case XOP_PUSH: mvars->ops[M] |= op & OP_MASK_TYPE; break; default: cops |= op; break; } mvars->ops[M] |= XOP_HAVE_TYPE; } continue; } ochar = argv[mvars->oind++] + 1; if (!*ochar) { error( "Invalid option '-'\n" ); return 1; } } switch (*ochar++) { case 'a': mvars->all = 1; break; case 'l': mvars->list = 1; break; case 'c': if (*ochar == 'T') { ochar++; pseudo = 1; } if (mvars->oind >= argc) { error( "-c requires an argument.\n" ); return 1; } config = argv[mvars->oind++]; break; case 'C': op = OP_CREATE|XOP_HAVE_CREATE; cop: if (*ochar == 'm') mvars->ops[M] |= op, ochar++; else if (*ochar == 's') mvars->ops[S] |= op, ochar++; else if (*ochar == '-') ochar++; else cops |= op; mvars->ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_EXPUNGE); break; case 'X': op = OP_EXPUNGE|XOP_HAVE_EXPUNGE; goto cop; case 'F': cops |= XOP_PULL|XOP_PUSH; case '0': mvars->ops[M] |= XOP_HAVE_TYPE; break; case 'n': case 'd': case 'f': case 'N': --ochar; op = 0; cac: for (;; ochar++) { if (*ochar == 'n') op |= OP_NEW; else if (*ochar == 'd') op |= OP_DELETE; else if (*ochar == 'f') op |= OP_FLAGS; else if (*ochar == 'N') op |= OP_RENEW; else break; } if (op & OP_MASK_TYPE) switch (op & XOP_MASK_DIR) { case XOP_PULL: mvars->ops[S] |= op & OP_MASK_TYPE; break; case XOP_PUSH: mvars->ops[M] |= op & OP_MASK_TYPE; break; default: cops |= op; break; } else cops |= op; mvars->ops[M] |= XOP_HAVE_TYPE; break; case 'L': op = XOP_PULL; goto cac; case 'H': op = XOP_PUSH; goto cac; case 'q': if (DFlags & QUIET) DFlags |= VERYQUIET; else DFlags |= QUIET; break; case 'V': if (DFlags & VERBOSE) DFlags |= XVERBOSE; else DFlags |= VERBOSE | QUIET; break; case 'D': DFlags |= DEBUG | QUIET; break; case 'J': DFlags |= KEEPJOURNAL; break; case 'v': version(); case 'h': usage( 0 ); default: error( "Unknown option '-%c'\n", *(ochar - 1) ); return 1; } } #ifdef __linux__ if (DFlags & DEBUG) { signal( SIGSEGV, crashHandler ); signal( SIGBUS, crashHandler ); signal( SIGILL, crashHandler ); } #endif if (merge_ops( cops, mvars->ops )) return 1; if (load_config( config, pseudo )) return 1; if (!mvars->all && !argv[mvars->oind]) { fputs( "No channel specified. Try '" EXE " -h'\n", stderr ); return 1; } if (!channels) { fputs( "No channels defined. Try 'man " EXE "'\n", stderr ); return 1; } mvars->chan = channels; if (mvars->all) mvars->multiple = channels->next != 0; else if (argv[mvars->oind + 1]) mvars->multiple = 1; else for (group = groups; group; group = group->next) if (!strcmp( group->name, argv[mvars->oind] )) { mvars->multiple = 1; break; } mvars->argv = argv; mvars->cben = 1; sync_chans( mvars, E_START ); return mvars->ret; }
int load_config( const char *where, int pseudo ) { conffile_t cfile; store_conf_t *store, **storeapp = &stores; channel_conf_t *channel, **channelapp = &channels; group_conf_t *group, **groupapp = &groups; string_list_t *chanlist, **chanlistapp; char *arg, *p; int len, cops, gcops, max_size, ms, i; char path[_POSIX_PATH_MAX]; char buf[1024]; if (!where) { assert( !pseudo ); nfsnprintf( path, sizeof(path), "%s/." EXE "rc", Home ); cfile.file = path; } else cfile.file = where; if (!pseudo) info( "Reading configuration file %s\n", cfile.file ); if (!(cfile.fp = fopen( cfile.file, "r" ))) { sys_error( "Cannot open config file '%s'", cfile.file ); return 1; } buf[sizeof(buf) - 1] = 0; cfile.buf = buf; cfile.bufl = sizeof(buf) - 1; cfile.line = 0; cfile.err = 0; cfile.rest = 0; gcops = 0; global_conf.expire_unread = -1; reloop: while (getcline( &cfile )) { if (!cfile.cmd) continue; for (i = 0; i < N_DRIVERS; i++) if (drivers[i]->parse_store( &cfile, &store )) { if (store) { if (!store->max_size) store->max_size = INT_MAX; *storeapp = store; storeapp = &store->next; *storeapp = 0; } goto reloop; } if (!strcasecmp( "Channel", cfile.cmd )) { channel = nfcalloc( sizeof(*channel) ); channel->name = nfstrdup( cfile.val ); channel->max_messages = global_conf.max_messages; channel->expire_unread = global_conf.expire_unread; channel->use_internal_date = global_conf.use_internal_date; cops = 0; max_size = -1; while (getcline( &cfile ) && cfile.cmd) { if (!strcasecmp( "MaxSize", cfile.cmd )) max_size = parse_size( &cfile ); else if (!strcasecmp( "Pattern", cfile.cmd ) || !strcasecmp( "Patterns", cfile.cmd )) { arg = cfile.val; do add_string_list( &channel->patterns, arg ); while ((arg = get_arg( &cfile, ARG_OPTIONAL, 0 ))); } else if (!strcasecmp( "Master", cfile.cmd )) { ms = M; goto linkst; } else if (!strcasecmp( "Slave", cfile.cmd )) { ms = S; linkst: if (*cfile.val != ':' || !(p = strchr( cfile.val + 1, ':' ))) { error( "%s:%d: malformed mailbox spec\n", cfile.file, cfile.line ); cfile.err = 1; continue; } *p = 0; for (store = stores; store; store = store->next) if (!strcmp( store->name, cfile.val + 1 )) { channel->stores[ms] = store; goto stpcom; } error( "%s:%d: unknown store '%s'\n", cfile.file, cfile.line, cfile.val + 1 ); cfile.err = 1; continue; stpcom: if (*++p) channel->boxes[ms] = nfstrdup( p ); } else if (!getopt_helper( &cfile, &cops, channel )) { error( "%s:%d: unknown keyword '%s'\n", cfile.file, cfile.line, cfile.cmd ); cfile.err = 1; } } if (!channel->stores[M]) { error( "channel '%s' refers to no master store\n", channel->name ); cfile.err = 1; } else if (!channel->stores[S]) { error( "channel '%s' refers to no slave store\n", channel->name ); cfile.err = 1; } else if (merge_ops( cops, channel->ops )) cfile.err = 1; else { if (max_size >= 0) { if (!max_size) max_size = INT_MAX; channel->stores[M]->max_size = channel->stores[S]->max_size = max_size; } *channelapp = channel; channelapp = &channel->next; } } else if (!strcasecmp( "Group", cfile.cmd )) { group = nfmalloc( sizeof(*group) ); group->name = nfstrdup( cfile.val ); *groupapp = group; groupapp = &group->next; *groupapp = 0; chanlistapp = &group->channels; *chanlistapp = 0; while ((arg = get_arg( &cfile, ARG_OPTIONAL, 0 ))) { addone: len = strlen( arg ); chanlist = nfmalloc( sizeof(*chanlist) + len ); memcpy( chanlist->string, arg, len + 1 ); *chanlistapp = chanlist; chanlistapp = &chanlist->next; *chanlistapp = 0; } while (getcline( &cfile )) { if (!cfile.cmd) goto reloop; if (!strcasecmp( "Channel", cfile.cmd ) || !strcasecmp( "Channels", cfile.cmd )) { arg = cfile.val; goto addone; } else { error( "%s:%d: unknown keyword '%s'\n", cfile.file, cfile.line, cfile.cmd ); cfile.err = 1; } } break; } else if (!strcasecmp( "FSync", cfile.cmd )) { UseFSync = parse_bool( &cfile ); } else if (!strcasecmp( "FieldDelimiter", cfile.cmd )) { if (strlen( cfile.val ) != 1) { error( "%s:%d: Field delimiter must be exactly one character long\n", cfile.file, cfile.line ); cfile.err = 1; } else { FieldDelimiter = cfile.val[0]; if (!ispunct( FieldDelimiter )) { error( "%s:%d: Field delimiter must be a punctuation character\n", cfile.file, cfile.line ); cfile.err = 1; } } } else if (!strcasecmp( "BufferLimit", cfile.cmd )) { BufferLimit = parse_size( &cfile ); if (BufferLimit <= 0) { error( "%s:%d: BufferLimit must be positive\n", cfile.file, cfile.line ); cfile.err = 1; } } else if (!getopt_helper( &cfile, &gcops, &global_conf )) { error( "%s:%d: unknown section keyword '%s'\n", cfile.file, cfile.line, cfile.cmd ); cfile.err = 1; while (getcline( &cfile )) if (!cfile.cmd) goto reloop; break; } } fclose (cfile.fp); cfile.err |= merge_ops( gcops, global_conf.ops ); if (!global_conf.sync_state) global_conf.sync_state = expand_strdup( "~/." EXE "/" ); if (!cfile.err && pseudo) unlink( where ); return cfile.err; }