ossp-pkg/lmtp2nntp/lmtp2nntp_option.c
/*
** OSSP lmtp2nntp - Mail to News Gateway
** Copyright (c) 2002-2003 Ralf S. Engelschall <rse@engelschall.com>
** Copyright (c) 2002-2003 The OSSP Project <http://www.ossp.org/>
** Copyright (c) 2002-2003 Cable & Wireless Germany <http://www.cw.com/de/>
**
** This file is part of OSSP lmtp2nntp, an LMTP speaking local
** mailer which forwards mails as Usenet news articles via NNTP.
** It can be found at http://www.ossp.org/pkg/tool/lmtp2nntp/.
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version
** 2.0 of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this file; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA, or contact the OSSP project <ossp@ossp.org>.
**
** lmtp2nntp_option.c: option parsing
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
/* third party (included) */
#include "lmtp2nntp_argz.h"
/* third party (linked in) */
#include "str.h"
#include "val.h"
#include "popt.h"
/* library version check (compile-time) */
#define STR_VERSION_HEX_REQ 0x009206
#define STR_VERSION_STR_REQ "0.9.6"
#ifdef STR_VERSION_HEX
#if STR_VERSION_HEX < STR_VERSION_HEX_REQ
#error "require a newer version of OSSP Str"
#endif
#endif
/* own headers */
#include "lmtp2nntp_global.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if defined(HAVE_DMALLOC_H) && defined(WITH_DMALLOC)
#include "dmalloc.h"
#endif
#include "lmtp2nntp_option.h"
#include "lmtp2nntp_config.h"
#include "lmtp2nntp_lmtp.h"
#include "lmtp2nntp_nntp.h"
#include "lmtp2nntp_msg.h"
#define _LMTP2NNTP_VERSION_C_AS_HEADER_
#include "lmtp2nntp_version.c"
#undef _LMTP2NNTP_VERSION_C_AS_HEADER_
#ifndef FALSE
#define FALSE (1 != 1)
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
#ifndef NUL
#define NUL '\0'
#endif
#if 0
static val_rc_t dumper(void *ctx, const char *name, int type, const char *desc, void *data)
{
optionval_t *oc;
int i;
if (type != VAL_TYPE_PTR)
return VAL_OK;
oc = *(optionval_t **)data;
switch (oc->type) {
case OPT_FLAG:
printf("DEBUG: <%5s>, name=<%20s>, OPT_FLAG, desc=<%20s>, data@%.8lx->[%d]%d\n", (char *)ctx, name, desc, (long)oc, oc->ndata, oc->data.f); break;
case OPT_SINGLE:
printf("DEBUG: <%5s>, name=<%20s>, OPT_SINGLE, desc=<%20s>, data@%.8lx->[%d]\"%s\"\n", (char *)ctx, name, desc, (long)oc, oc->ndata, oc->data.s == NULL ? "NULL" : oc->data.s);
break;
case OPT_MULTI:
printf("DEBUG: <%5s>, name=<%20s>, OPT_MULTI, desc=<%20s>, data@%.8lx->[%d]%.8lx\n", (char *)ctx, name, desc, (long)oc, oc->ndata, (long)oc->data.m);
for (i = 0; i < oc->ndata; i++) {
{
int j;
printf("DEBUG: ");
for (j=0; j<8; j++) printf("%.2x ", (unsigned char)oc->data.m[i][j]);
printf(" ");
for (j=0; j<8; j++) printf("%c", isprint(oc->data.m[i][j]) ? oc->data.m[i][j] : '.');
printf(" ");
}
printf("DEBUG: [%3d] %.8lx \"%s\"\n", i, (long)oc->data.m[i], oc->data.m[i]);
}
break;
default:
break;
}
return VAL_OK;
}
#endif
static lmtp2nntp_option_rc_t option_find(lmtp2nntp_option_t *o, int number, optionval_t **ocp)
{
if (o == NULL || ocp == NULL)
return OPTION_ERR_ARG;
*ocp = o->first;
while (*ocp != NULL && (*ocp)->number != number)
*ocp = (*ocp)->next;
if (*ocp == NULL)
return OPTION_ERR_NUM;
return OPTION_OK;
}
static void option_register(lmtp2nntp_option_t *o, char *longname, char shortname, optiontype_t type, char *def, char *descrip, char *argdescrip, optionloop_cb_t *cb, char *cbctx)
{
volatile struct {
optionval_t *oc;
} v;
ex_t ex;
v.oc = NULL;
try {
if (o == NULL || longname == NULL)
throw(option_register, o, OPTION_ERR_ARG);
/* create a optionval_t structure and initialize exception-uncritical data */
v.oc = (optionval_t *)mallocex(sizeof(optionval_t));
v.oc->next = NULL;
v.oc->parent = o;
v.oc->longname = NULL;
v.oc->shortname = shortname;
v.oc->descrip = NULL;
v.oc->argdescrip = NULL;
v.oc->type = type;
v.oc->cb = cb;
v.oc->cbctx = cbctx;
v.oc->val = o->vo;
v.oc->number = o->pi + 1; /* 0 is a reserved val in popt, so offset 1 */
switch (type) {
case OPT_FLAG: v.oc->data.f = 0; break;
case OPT_SINGLE: v.oc->data.s = NULL; break;
case OPT_MULTI: v.oc->data.m = NULL; break;
}
v.oc->ndata = 0;
/* preinitialization complete, now initialize exception-critical data*/
v.oc->longname = strdupex(longname);
if (descrip != NULL)
v.oc->descrip = strdupex(descrip);
if (argdescrip != NULL)
v.oc->argdescrip = strdupex(argdescrip);
if (type == OPT_SINGLE && def != NULL) {
v.oc->data.s = strdupex(def);
v.oc->ndata = 1;
}
/* feed lib_val */
if (val_reg(v.oc->val, v.oc->longname, VAL_TYPE_PTR, v.oc->descrip, NULL) != VAL_OK)
throw(option_register, o, OPTION_ERR_USE);
if (val_set(v.oc->val, v.oc->longname, v.oc) != VAL_OK)
throw(option_register, o, OPTION_ERR_USE);
/* feed lib_popt */
if (o->pi >= (o->pn-2)) { /* correction by two here, in mallv.oc and reallv.oc is for AUTOHELP and TABLEEND */
if (o->pt == NULL) {
o->pt = (struct popt_option *)mallocex( (1 + 2) * sizeof(struct popt_option));
o->pn = 1;
}
else {
o->pt = (struct popt_option *)reallocex(o->pt, (o->pn * 2 + 2) * sizeof(struct popt_option));
o->pn = o->pn * 2;
}
}
o->pt[o->pi].longName = v.oc->longname;
o->pt[o->pi].shortName = v.oc->shortname;
o->pt[o->pi].argInfo = v.oc->type == OPT_FLAG ? POPT_ARG_NONE : POPT_ARG_STRING;
o->pt[o->pi].arg = NULL;
o->pt[o->pi].val = v.oc->number;
o->pt[o->pi].descrip = v.oc->descrip;
o->pt[o->pi].argDescrip = v.oc->argdescrip;
/* append POPT_AUTOHELP */
o->pt[o->pi+1].longName = NULL;
o->pt[o->pi+1].shortName = '\0';
o->pt[o->pi+1].argInfo = POPT_ARG_INCLUDE_TABLE;
o->pt[o->pi+1].arg = popt_helpoptions;
o->pt[o->pi+1].val = 0;
o->pt[o->pi+1].descrip = "Help options:";
o->pt[o->pi+1].argDescrip = NULL;
/* append POPT_TABLEEND */
o->pt[o->pi+2].longName = NULL;
o->pt[o->pi+2].shortName = '\0';
o->pt[o->pi+2].argInfo = 0;
o->pt[o->pi+2].arg = 0;
o->pt[o->pi+2].val = 0;
o->pt[o->pi+2].descrip = NULL;
o->pt[o->pi+2].argDescrip = NULL;
o->pi++;
/* link in this new optionval_t structure */
if (o->first == NULL) {
o->first = v.oc;
o->last = v.oc;
}
else {
o->last->next = v.oc;
o->last = v.oc;
}
}
catch(ex) {
int i;
if (v.oc != NULL) {
if (type == OPT_SINGLE && v.oc->data.s != NULL)
free(v.oc->data.s);
if (type == OPT_MULTI && v.oc->data.m != NULL) {
for (i = 0; i < v.oc->ndata; i++)
if (v.oc->data.m[i] == NULL)
break;
else
free(v.oc->data.m[i]);
free(v.oc->data.m);
}
if (v.oc->argdescrip != NULL)
free(v.oc->argdescrip);
if (v.oc->descrip != NULL)
free(v.oc->descrip);
if (v.oc->longname != NULL)
free(v.oc->longname);
free(v.oc);
}
rethrow;
}
return;
}
/* this public function catches all underlying exceptions and properly returns a code */
lmtp2nntp_option_rc_t option_create(lmtp2nntp_option_t **op, val_t *parent)
{
ex_t ex;
try {
if (op == NULL || parent == NULL)
return OPTION_ERR_ARG;
(*op = (lmtp2nntp_option_t *)mallocex(sizeof(lmtp2nntp_option_t)));
(*op)->first = NULL;
(*op)->last = NULL;
(*op)->vo = NULL;
(*op)->pi = 0;
(*op)->pn = 0;
(*op)->pt = NULL;
if (val_create(&((*op)->vo)) != VAL_OK)
return OPTION_ERR_VAL;
if (val_reg(parent, "option", VAL_TYPE_VAL, "option", (void *)&((*op)->vo)) != VAL_OK)
return OPTION_ERR_VAL;
}
catch(ex) {
if (*op != NULL) {
if ((*op)->vo != NULL)
val_unreg(parent, "option");
free(*op);
}
if (ex.ex_class == (void *)option_create)
return (lmtp2nntp_option_rc_t)ex.ex_value;
return OPTION_ERR_TRY;
}
return OPTION_OK;
}
static lmtp2nntp_option_rc_t option_parse_internal(lmtp2nntp_option_t *o, int argc, char **argv)
{
lmtp2nntp_option_rc_t rc = OPTION_OK;
lmtp2nntp_option_rc_t rv;
int i;
char *cp;
optionval_t *ocp;
popt_context poptCon;
/* internal function trusts args */
/* init lib_popt */
poptCon = popt_getcontext(NULL, argc, (const char **)argv, o->pt, 0);
popt_setotheroptionhelp(poptCon, "[OPTIONS]* [newsgroup ...]");
/* print usage if too few argv's */
if (argc < 2) {
popt_printusage(poptCon, stderr, 0);
return OPTION_ERR_USE;
}
/* parse every option, continue when optarg missing or bad option found */
while ((i = popt_getnextopt(poptCon)) >= 0 || i == POPT_ERROR_NOARG || i == POPT_ERROR_BADOPT) {
if (i == POPT_ERROR_NOARG || i == POPT_ERROR_BADOPT) {
fprintf(stderr, "ERROR: %s (%d) \"%s\"\n", popt_strerror(i), i, popt_badoption(poptCon, POPT_BADOPTION_NOALIAS));
rc = OPTION_ERR_USE;
continue;
}
if ((option_find(o, i, &ocp) == OPTION_OK) && (ocp->cb != NULL)) {
rv = ocp->cb(ocp, cp = (ocp->type == OPT_FLAG ? NULL : (char *)popt_getoptarg(poptCon)), ocp->cbctx);
if (cp != NULL)
free(cp); /* TODO this should be the task of popt_freecontext but debugging showed popt does not free(3) */
if (rc == OPTION_OK)
rc = rv;
}
}
/* create a "--newsgroup" argc/argv for every leftover argument */
{
ex_t ex;
volatile struct {
char **largv;
} v;
try {
int largc;
v.largv = (char **)mallocex((1 + 1) * sizeof(char **));
largc = 0;
v.largv[largc] = NULL;
v.largv[largc++] = strdupex("leftover");
v.largv[largc] = NULL;
while ((cp = (char *)popt_getarg(poptCon)) != NULL) {
v.largv = (char **)reallocex(v.largv, (1 + largc + 2) * sizeof(char **));
v.largv[largc++] = strdupex("--newsgroup");
v.largv[largc] = NULL;
v.largv[largc++] = strdupex(cp);
v.largv[largc] = NULL;
}
if (largc > 1) {
rv = option_parse_internal(o, largc, v.largv);
if (rc == OPTION_OK)
rc = rv;
}
}
cleanup {
if (v.largv != NULL) {
for (i = 0; v.largv[i] != NULL; i++)
free(v.largv[i]);
free(v.largv);
}
}
catch(ex) {
rc = OPTION_ERR_TRY;
}
}
popt_freecontext(poptCon);
return rc;
}
static lmtp2nntp_option_rc_t stdsyntax(optionval_t *oc, char *arg, char *cbctx)
{
switch (oc->type) {
case OPT_FLAG:
if (arg != NULL || cbctx != NULL)
return OPTION_ERR_ARG;
if (oc->ndata >= 1)
return OPTION_ERR_USE;
oc->data.f = 1;
oc->ndata = 1;
break;
case OPT_SINGLE:
if (arg == NULL)
return OPTION_ERR_ARG;
/* add this if repeated overwriting definitions of single values is not allowed
* however, this will inhibit preinitialization with a default value
* if (oc->ndata != 0)
* return OPTION_ERR_USE;
*/
if (oc->ndata == 1 && oc->data.s != NULL) { /* free previous (default) assignment */
free(oc->data.s);
oc->data.s = NULL;
oc->ndata = 0;
}
if (cbctx != NULL)
if (str_parse(arg, cbctx) <= 0) {
fprintf(stderr, "ERROR: argument \"%s\" does NOT match syntax \"%s\"\n", arg, cbctx);
return OPTION_ERR_USE;
}
if ((oc->data.s = strdup(arg)) == NULL)
return OPTION_ERR_MEM;
oc->ndata = 1;
break;
case OPT_MULTI:
if (arg == NULL)
return OPTION_ERR_ARG;
if (oc->ndata >= 1 && oc->data.m == NULL)
return OPTION_ERR_USE;
if (cbctx != NULL)
if (str_parse(arg, cbctx) <= 0) {
fprintf(stderr, "ERROR: argument \"%s\" does NOT match syntax \"%s\"\n", arg, cbctx);
return OPTION_ERR_USE;
}
if (oc->data.m == NULL) /* existing + this new + terminating NULL */
oc->data.m = (char **)mallocex( ( 0 + 1 + 1) * sizeof(char **));
else
oc->data.m = (char **)reallocex(oc->data.m, (oc->ndata + 1 + 1) * sizeof(char **));
oc->data.m[oc->ndata] = strdupex(arg);
oc->ndata++;
oc->data.m[oc->ndata] = NULL;
break;
default:
return OPTION_ERR_ARG;
break;
}
return OPTION_OK;
}
static lmtp2nntp_option_rc_t includeit(optionval_t *oc, char *arg, char *cbctx)
{
lmtp2nntp_option_rc_t rc = OPTION_OK;
lmtp2nntp_option_t *o;
volatile char *cpBuf = NULL;
int argc = 0;
char **argv = NULL;
const char *filename = arg;
struct stat sb;
volatile int fd = -1;
ex_t ex;
char *cpI; /* pointer to next character to be read */
char *cpO; /* pointer to next character to be written. Used for eliminating backslash+newline at a line continuation */
char *cpL; /* pointer to start of line */
int pline; /* current physical (disregarding line continuation) line number */
int lline; /* current logical lines first physical line number */
int eline; /* flag signaling empty or just whitespace-filled line */
char c; /* current character */
char p; /* previous character */
int eof; /* flag signaling end of file detected */
if ((o = oc->parent) == NULL)
return OPTION_ERR_USE;
try {
if (stdsyntax(oc, arg, cbctx) != OPTION_OK)
throw(0, 0, 0);
if (stat(filename, &sb) == -1)
throw(includeit, oc, "stat");
if (sb.st_size == 0)
throw(includeit, oc, "size");
cpBuf = (char *)mallocex((size_t)sb.st_size + 1);
if ((fd = open(filename, O_RDONLY)) == -1)
throw(includeit, oc, "open");
if (read(fd, (void *)cpBuf, (size_t)sb.st_size) != (ssize_t)sb.st_size)
throw(includeit, oc, "read");
cpBuf[(int)sb.st_size] = '\0';
cpI = (char *)cpBuf;
cpO = (char *)cpBuf;
eof = FALSE;
pline = 1;
p = NUL;
cpL = cpO;
lline = pline;
eline = TRUE;
while(!eof) {
c = *cpI++;
*cpO++ = c;
if (c == NUL)
eof = TRUE;
else
if (!isspace(c))
eline = FALSE;
if (eof || (c == '\n')) {
pline++;
if (!eof && (p == '\\')) { /* line continuation situation */
cpO-=2; /* need to remove both backslash+newline */
}
else {
if (!eline) { /* process logical line unless it's empty */
*(cpO-1) = NUL;
if (lline == (pline-1))
;/*printf("DEBUG: line[%3d] = ***%s***\n", lline, cpL);*/
else
;/*printf("DEBUG: [%3d-%3d] = ***%s***\n", lline, pline-1, cpL);*/
{
char *cp = cpL;
char *option;
char *value;
char *cpNew;
argz_t Newarg;
Newarg.as = 0;
Newarg.az = NULL;
if ((option = str_token(&cp, " \t", "\"'", "#", STR_STRIPQUOTES|STR_BACKSLASHESC)) == NULL)
;/* no option? comment only! don't care about that. */
else {
/* create fake argv[0] */
if (argv == NULL) {
argv = (char **)mallocex((1 + 1) * sizeof(char **)); /* argv[0] + NULL */
argv[argc++] = strdupex("include");
argv[argc] = NULL;
}
/* handle option */
cpNew = (char *)mallocex(2 + strlen(option) + 1); /* dash dash option[] NUL */
cpNew[0]=NUL;
strcat(cpNew, "--");
strcat(cpNew, option);
argv = (char **)reallocex(argv, (1 + argc + 1) * sizeof(char **)); /* argv[0] + argv[1...argc] + NULL */
argv[argc++] = cpNew;
argv[argc] = NULL;
if ((value = str_token(&cp, " \t", "\"'", "#", STR_STRIPQUOTES|STR_BACKSLASHESC)) == NULL)
;/* no value? optional anyway */
else {
while(isspace((int)*value)) value++;
/* handle value */
cpNew = strdupex(value);
argv = (char **)reallocex(argv, (1 + argc + 1 + 1) * sizeof(char **)); /* argv[0] + argv[1...argc] + NULL */
argv[argc++] = cpNew;
argv[argc] = NULL;
}
}
}
}
cpL = cpO;
lline = pline;
eline = TRUE;
}
}
p = c;
}
rc = option_parse_internal(o, argc, argv);
}
cleanup {
int i;
if (fd != -1)
close(fd);
if (argv != NULL) {
for (i = 0; argv[i] != NULL; i++) {
free(argv[i]);
}
free(argv);
}
if (cpBuf != NULL)
free((char *)cpBuf);
}
catch(ex) {
if (ex.ex_class == (void *)includeit && ex.ex_value != NULL) {
fprintf(stderr, "ERROR: problem with \"%s\" while including \"%s\"\n", (char *)ex.ex_value, filename);
}
rc = OPTION_ERR_TRY;
}
return rc;
}
/* this public function catches all underlying exceptions and properly returns a code */
lmtp2nntp_option_rc_t option_parse(lmtp2nntp_option_t *o, int argc, char **argv)
{
ex_t ex;
if (o == NULL || argc < 0 || argv == NULL)
return OPTION_ERR_ARG;
try {
option_register(o, "childsmax", 'C', OPT_SINGLE, "10", "Childs the daemon spawns at max.", "childsmax", &stdsyntax, "m/\\d+/" ); /*"m/[0-9]+/"*/
option_register(o, "daemonize", 'D', OPT_FLAG, NULL, "Daemonize and detach from terminal", NULL, &stdsyntax, NULL );
option_register(o, "kill", 'K', OPT_FLAG, NULL, "Kill a previously run daemon", NULL, &stdsyntax, NULL );
option_register(o, "pidfile", 'P', OPT_SINGLE, NULL, "Pidfile holding the process ID", "filename", &stdsyntax, "m/.*/" );
option_register(o, "acl", 'a', OPT_MULTI, NULL, "LMTP daemon access control list", "addr[/mask]", &stdsyntax, "m/.*/" );
option_register(o, "bind", 'b', OPT_SINGLE, NULL, "LMTP daemon bind", "addr[:port]|-|path[:perms]", &stdsyntax, "m/.*/" );
option_register(o, "client", 'c', OPT_SINGLE, NULL, "NNTP client bind", "addr[:port]", &stdsyntax, "m/.*/" );
option_register(o, "destination", 'd', OPT_MULTI, NULL, "NNTP client destination", "addr[:port]", &stdsyntax, "m/.*/" );
option_register(o, "groupmode", 'g', OPT_SINGLE, "arg", "Groupmode configures news group(s)", "arg|envelope|header", &stdsyntax, "m/.*/" ); /*"m/(arg|envelope|header)/"*/
option_register(o, "headerrule", 'h', OPT_MULTI, NULL, "Header rewriting rule", "[pri]:[regex]:header:[val]", &stdsyntax, "m/^[0-9]*:.*:.+:.*$/" );
option_register(o, "include", 'i', OPT_MULTI, NULL, "Include a configuration file", "configfile", &includeit, "m/.*/" );
option_register(o, "l2spec", 'l', OPT_SINGLE, NULL, "L2 channel tree specification", "l2spec", &stdsyntax, "m/.*/" );
option_register(o, "mailfrom", 'm', OPT_SINGLE, NULL, "Mail from envelope restriction", "regex", &stdsyntax, "m/.*/" );
option_register(o, "nodename", 'n', OPT_SINGLE, NULL, "System nodename", "name", &stdsyntax, "m/.*/" );
option_register(o, "operationmode", 'o', OPT_SINGLE, "553/5.7.1", "Set fake status or operationmode", "abc/a.d.e|post|feed", &stdsyntax, "m/.*/" ); /*"m/([0-9]{3}\\/[0-9]\\.[0-9]\\.[0-9]|post|feed)/" ); 553 = Requested action not taken: mailbox name not allowed, 5.7.1 = Delivery not authorized, message refused */
option_register(o, "restrictheader", 'r', OPT_SINGLE, NULL, "Restrict messages by header", "regex", &stdsyntax, "m/.*/" );
option_register(o, "size", 's', OPT_SINGLE, "8388608", "Size limitation on message", "bytes", &stdsyntax, "m/.*/" ); /*"m/[0-9]+/"*/
option_register(o, "testfile", 't', OPT_MULTI, NULL, "Testfile for headerrule", "filename", &stdsyntax, "m/.*/" );
option_register(o, "timeoutlmtp", NUL, OPT_SINGLE, NULL, "LMTP server default timeout", "sec", &stdsyntax, "m/.*/" );
option_register(o, "timeoutlmtpaccept", NUL, OPT_SINGLE, "0", "LMTP server accept timeout", "sec", &stdsyntax, "m/.*/" );
option_register(o, "timeoutlmtpread", NUL, OPT_SINGLE, "10", "LMTP server read timeout", "sec", &stdsyntax, "m/.*/" );
option_register(o, "timeoutlmtpwrite", NUL, OPT_SINGLE, "10", "LMTP server write timeout", "sec", &stdsyntax, "m/.*/" );
option_register(o, "timeoutnntp", NUL, OPT_SINGLE, NULL, "NNTP client default timeout", "sec", &stdsyntax, "m/.*/" );
option_register(o, "timeoutnntpconnect", NUL, OPT_SINGLE, "360", "NNTP client connect timeout", "sec", &stdsyntax, "m/.*/" );
option_register(o, "timeoutnntpread", NUL, OPT_SINGLE, "60", "NNTP client read timeout", "sec", &stdsyntax, "m/.*/" );
option_register(o, "timeoutnntpwrite", NUL, OPT_SINGLE, "60", "NNTP client write timeout", "sec", &stdsyntax, "m/.*/" );
option_register(o, "user", 'u', OPT_SINGLE, NULL, "User identity", "uid|name", &stdsyntax, "m/.*/" );
option_register(o, "version", 'v', OPT_FLAG, NULL, "Version information", NULL, &stdsyntax, NULL );
option_register(o, "newsgroup", NUL, OPT_MULTI, NULL, "Newsgroup name or match", "newsgroup|wildmat", &stdsyntax, "m/.*/" );
}
catch(ex) {
if (ex.ex_class == (void *)option_create)
return (lmtp2nntp_option_rc_t)ex.ex_value;
return OPTION_ERR_TRY;
}
return option_parse_internal(o, argc, argv);
}
/* this public function catches all underlying exceptions and properly returns a code */
lmtp2nntp_option_rc_t option_destroy(lmtp2nntp_option_t *o)
{
optionval_t *oc;
optionval_t *ocn;
int i;
if (o == NULL)
return OPTION_ERR_ARG;
oc = o->first;
while (oc != NULL) {
if (oc->type == OPT_SINGLE && oc->data.s != NULL) {
free(oc->data.s);
}
if (oc->type == OPT_MULTI && oc->data.m != NULL) {
for (i = 0; i < oc->ndata; i++)
if (oc->data.m[i] == NULL)
break;
else
free(oc->data.m[i]);
free(oc->data.m);
}
if (oc->argdescrip != NULL)
free(oc->argdescrip);
if (oc->descrip != NULL)
free(oc->descrip);
if (oc->longname != NULL)
free(oc->longname);
ocn = oc->next;
free(oc);
oc = ocn;
}
if (o->vo != NULL)
val_destroy(o->vo);
if (o->pt != NULL)
free(o->pt);
free(o);
return OPTION_OK;
}