shared/cfgparser.c
e3aaff8e
 /*
8ca8a18e
  *  Copyright (C) 2002 - 2007 Tomasz Kojm <tkojm@clamav.net>
e3aaff8e
  *
  *  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 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 program; if not, write to the Free Software
48b7b4a7
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
e3aaff8e
  */
 
6d6e8271
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
e3aaff8e
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
 
a889f40e
 #include "shared/cfgparser.h"
 #include "shared/misc.h"
e3aaff8e
 
a889f40e
 #include "libclamav/str.h"
e628b432
 
 struct cfgoption cfg_options[] = {
5174f778
     {"LogFile",	OPT_QUOTESTR, -1, NULL, 0, OPT_CLAMD},
e628b432
     {"LogFileUnlock", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
     {"LogFileMaxSize", OPT_COMPSIZE, 1048576, NULL, 0, OPT_CLAMD},
     {"LogTime", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
     {"LogClean", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
     {"LogVerbose", OPT_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM},
     {"LogSyslog", OPT_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM},
eaa9dbda
     {"LogFacility", OPT_QUOTESTR, -1, "LOG_LOCAL6", 0, OPT_CLAMD | OPT_FRESHCLAM},
5174f778
     {"PidFile", OPT_QUOTESTR, -1, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM},
     {"TemporaryDirectory", OPT_QUOTESTR, -1, NULL, 0, OPT_CLAMD},
e628b432
     {"ScanPE", OPT_BOOL, 1, NULL, 0, OPT_CLAMD},
3f97a1e7
     {"ScanELF", OPT_BOOL, 1, NULL, 0, OPT_CLAMD},
e628b432
     {"DetectBrokenExecutables", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
     {"ScanMail", OPT_BOOL, 1, NULL, 0, OPT_CLAMD},
     {"MailFollowURLs", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
dab42957
     {"MailMaxRecursion", OPT_NUM, 64, NULL, 0, OPT_CLAMD},
af7d0dde
     {"PhishingSignatures", OPT_BOOL, 1, NULL, 0, OPT_CLAMD},
a68507c5
     {"PhishingScanURLs",OPT_BOOL, 1, NULL, 0, OPT_CLAMD},
19b3e182
     /* these are FP prone options, if default isn't used */
     {"PhishingAlwaysBlockCloak", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
     {"PhishingAlwaysBlockSSLMismatch", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
     {"PhishingRestrictedScan", OPT_BOOL, 1, NULL, 0, OPT_CLAMD},
     /* end of FP prone options */
47138a98
     {"AlgorithmicDetection", OPT_BOOL, 1, NULL, 0, OPT_CLAMD},
e628b432
     {"ScanHTML", OPT_BOOL, 1, NULL, 0, OPT_CLAMD},
     {"ScanOLE2", OPT_BOOL, 1, NULL, 0, OPT_CLAMD},
c5107e70
     {"ScanPDF", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
e628b432
     {"ScanArchive", OPT_BOOL, 1, NULL, 0, OPT_CLAMD},
     {"ArchiveMaxFileSize", OPT_COMPSIZE, 10485760, NULL, 0, OPT_CLAMD},
     {"ArchiveMaxRecursion", OPT_NUM, 8, NULL, 0, OPT_CLAMD},
     {"ArchiveMaxFiles", OPT_NUM, 1000, NULL, 0, OPT_CLAMD},
     {"ArchiveMaxCompressionRatio", OPT_NUM, 250, NULL, 0, OPT_CLAMD},
     {"ArchiveLimitMemoryUsage", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
     {"ArchiveBlockEncrypted", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
     {"ArchiveBlockMax", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
5174f778
     {"DatabaseDirectory", OPT_QUOTESTR, -1, DATADIR, 0, OPT_CLAMD | OPT_FRESHCLAM},
eaa9dbda
     {"TCPAddr", OPT_QUOTESTR, -1, NULL, 0, OPT_CLAMD},
e628b432
     {"TCPSocket", OPT_NUM, -1, NULL, 0, OPT_CLAMD},
8f4dd285
     {"LocalSocket", OPT_QUOTESTR, -1, NULL, 0, OPT_CLAMD},
e628b432
     {"MaxConnectionQueueLength", OPT_NUM, 15, NULL, 0, OPT_CLAMD},
     {"StreamMaxLength", OPT_COMPSIZE, 10485760, NULL, 0, OPT_CLAMD},
     {"StreamMinPort", OPT_NUM, 1024, NULL, 0, OPT_CLAMD},
     {"StreamMaxPort", OPT_NUM, 2048, NULL, 0, OPT_CLAMD},
     {"MaxThreads", OPT_NUM, 10, NULL, 0, OPT_CLAMD},
     {"ReadTimeout", OPT_NUM, 120, NULL, 0, OPT_CLAMD},
     {"IdleTimeout", OPT_NUM, 30, NULL, 0, OPT_CLAMD},
     {"MaxDirectoryRecursion", OPT_NUM, 15, NULL, 0, OPT_CLAMD},
     {"FollowDirectorySymlinks", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
     {"FollowFileSymlinks", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
     {"ExitOnOOM", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
     {"Foreground", OPT_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM},
     {"Debug", OPT_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM},
     {"LeaveTemporaryFiles", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
     {"FixStaleSocket", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
eaa9dbda
     {"User", OPT_QUOTESTR, -1, NULL, 0, OPT_CLAMD},
e628b432
     {"AllowSupplementaryGroups", OPT_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM},
     {"SelfCheck", OPT_NUM, 1800, NULL, 0, OPT_CLAMD},
     {"VirusEvent", OPT_FULLSTR, -1, NULL, 0, OPT_CLAMD},
b5456d64
     {"NodalCoreAcceleration", OPT_BOOL, 0, NULL, 0, OPT_CLAMD},
0bbe0584
     {"ClamukoScanOnAccess", OPT_BOOL, -1, NULL, 0, OPT_CLAMD},
     {"ClamukoScanOnOpen", OPT_BOOL, -1, NULL, 0, OPT_CLAMD},
     {"ClamukoScanOnClose", OPT_BOOL, -1, NULL, 0, OPT_CLAMD},
     {"ClamukoScanOnExec", OPT_BOOL, -1, NULL, 0, OPT_CLAMD},
9373401e
     {"ClamukoIncludePath", OPT_QUOTESTR, -1, NULL, 1, OPT_CLAMD},
     {"ClamukoExcludePath", OPT_QUOTESTR, -1, NULL, 1, OPT_CLAMD},
e628b432
     {"ClamukoMaxFileSize", OPT_COMPSIZE, 5242880, NULL, 0, OPT_CLAMD},
eaa9dbda
     {"DatabaseOwner", OPT_QUOTESTR, -1, CLAMAVUSER, 0, OPT_FRESHCLAM},
e628b432
     {"Checks", OPT_NUM, 12, NULL, 0, OPT_FRESHCLAM},
5174f778
     {"UpdateLogFile", OPT_QUOTESTR, -1, NULL, 0, OPT_FRESHCLAM},
eaa9dbda
     {"DNSDatabaseInfo", OPT_QUOTESTR, -1, "current.cvd.clamav.net", 0, OPT_FRESHCLAM},
     {"DatabaseMirror", OPT_QUOTESTR, -1, NULL, 1, OPT_FRESHCLAM},
e628b432
     {"MaxAttempts", OPT_NUM, 3, NULL, 0, OPT_FRESHCLAM},
011b4f29
     {"ScriptedUpdates", OPT_BOOL, 1, NULL, 0, OPT_FRESHCLAM},
eaa9dbda
     {"HTTPProxyServer", OPT_QUOTESTR, -1, NULL, 0, OPT_FRESHCLAM},
e628b432
     {"HTTPProxyPort", OPT_NUM, -1, NULL, 0, OPT_FRESHCLAM},
eaa9dbda
     {"HTTPProxyUsername", OPT_QUOTESTR, -1, NULL, 0, OPT_FRESHCLAM},
     {"HTTPProxyPassword", OPT_QUOTESTR, -1, NULL, 0, OPT_FRESHCLAM},
e628b432
     {"HTTPUserAgent", OPT_FULLSTR, -1, NULL, 0, OPT_FRESHCLAM},
8f4dd285
     {"NotifyClamd", OPT_QUOTESTR, -1, NULL, 0, OPT_FRESHCLAM},
e628b432
     {"OnUpdateExecute", OPT_FULLSTR, -1, NULL, 0, OPT_FRESHCLAM},
     {"OnErrorExecute", OPT_FULLSTR, -1, NULL, 0, OPT_FRESHCLAM},
     {"OnOutdatedExecute", OPT_FULLSTR, -1, NULL, 0, OPT_FRESHCLAM},
eaa9dbda
     {"LocalIPAddress", OPT_QUOTESTR, -1, NULL, 0, OPT_FRESHCLAM},
c27b1e2f
     {"ConnectTimeout", OPT_NUM, 30, NULL, 0, OPT_FRESHCLAM},
7d1c492d
     {"ReceiveTimeout", OPT_NUM, 30, NULL, 0, OPT_FRESHCLAM},
e628b432
     {NULL, 0, 0, NULL, 0, 0}
 };
 
fc83da82
 static int regcfg(struct cfgstruct **copt, const char *optname, char *strarg, int numarg, short multiple);
81837459
 
 struct cfgstruct *getcfg(const char *cfgfile, int verbose)
e3aaff8e
 {
2e988965
 	char buff[LINE_LENGTH], *name, *arg, *c;
e3aaff8e
 	FILE *fs;
81837459
 	int line = 0, i, found, ctype, calc, val;
e3aaff8e
 	struct cfgstruct *copt = NULL;
 	struct cfgoption *pt;
 
 
81837459
     for(i = 0; ; i++) {
 	pt = &cfg_options[i];
 	if(!pt->name)
 	    break;
 
52c3c225
 	if(regcfg(&copt, pt->name, pt->strarg ? strdup(pt->strarg) : NULL, pt->numarg, pt->multiple) < 0) {
81837459
 	    fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n");
 	    freecfg(copt);
 	    return NULL;
 	}
     }
 
109bde85
     if((fs = fopen(cfgfile, "rb")) == NULL) {
81837459
 	/* do not print error message here! */
 	freecfg(copt);
e3aaff8e
 	return NULL;
81837459
     }
e3aaff8e
 
     while(fgets(buff, LINE_LENGTH, fs)) {
 	line++;
 
 	if(buff[0] == '#')
 	    continue;
 
 	if(!strncmp("Example", buff, 7)) {
81837459
 	    if(verbose)
cc71d7c2
 		fprintf(stderr, "ERROR: Please edit the example config file %s.\n", cfgfile);
bcf3dc79
 	    fclose(fs);
81837459
 	    freecfg(copt);
e3aaff8e
 	    return NULL;
 	}
 
2d70a403
 	if((name = cli_strtok(buff, 0, " \r\n"))) {
 	    arg = cli_strtok(buff, 1, " \r\n");
e3aaff8e
 	    found = 0;
 	    for(i = 0; ; i++) {
 		pt = &cfg_options[i];
 		if(pt->name) {
 		    if(!strcmp(name, pt->name)) {
 			found = 1;
 			switch(pt->argtype) {
 			    case OPT_STR:
eaa9dbda
 			    	/* deprecated.  Use OPT_QUOTESTR instead since it behaves like this, but supports quotes to allow values to contain whitespace */
e3aaff8e
 				if(!arg) {
81837459
 				    if(verbose)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires string argument.\n", line, name);
bcf3dc79
 				    fclose(fs);
8de0d031
 				    free(name);
81837459
 				    freecfg(copt);
 				    return NULL;
 				}
 				if(regcfg(&copt, name, arg, -1, pt->multiple) < 0) {
 				    fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n");
 				    fclose(fs);
8de0d031
 				    free(name);
 				    free(arg);
81837459
 				    freecfg(copt);
e3aaff8e
 				    return NULL;
 				}
 				break;
0249f9d2
 			    case OPT_FULLSTR:
81837459
 				/* an ugly hack of the above case */
0249f9d2
 				if(!arg) {
81837459
 				    if(verbose)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires string argument.\n", line, name);
bcf3dc79
 				    fclose(fs);
8de0d031
 				    free(name);
81837459
 				    freecfg(copt);
0249f9d2
 				    return NULL;
 				}
 				free(arg);
 				arg = strstr(buff, " ");
 				arg = strdup(++arg);
8de0d031
 				if((arg) && (c = strpbrk(arg, "\n\r")))
2e988965
 				    *c = '\0';
8de0d031
 				if((!arg) || (regcfg(&copt, name, arg, -1, pt->multiple) < 0)) {
81837459
 				    fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n");
 				    fclose(fs);
8de0d031
 				    free(name);
 				    free(arg);
81837459
 				    freecfg(copt);
 				    return NULL;
 				}
0249f9d2
 				break;
5174f778
 			    case OPT_QUOTESTR:
 				/* an ugly hack of the above case */
 				if(!arg) {
 				    if(verbose)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires string argument.\n", line, name);
 				    fclose(fs);
 				    free(name);
 				    freecfg(copt);
 				    return NULL;
 				}
 				if((*arg == '\'') || (*arg == '"')) {
 				    free(arg);
 				    c = strstr(buff, " ");
 				    arg = strdup(c+2);
 				    if(arg) {
 					if((c = strchr(arg, c[1])))
 					    *c = '\0';
 					else {
 					    if(verbose)
 						fprintf(stderr, "ERROR: Parse error at line %d: Option %s missing closing quote.\n", line, name);
 					    fclose(fs);
 					    free(name);
 					    free(arg);
 					    freecfg(copt);
 					    return NULL;
 					}
 				    }
 				}
 				if((!arg) || (regcfg(&copt, name, arg, -1, pt->multiple) < 0)) {
 				    fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n");
 				    fclose(fs);
 				    free(name);
 				    free(arg);
 				    freecfg(copt);
 				    return NULL;
 				}
 				break;
e3aaff8e
 			    case OPT_NUM:
0deebced
 				if(!arg || !isnumb(arg)) {
81837459
 				    if(verbose)
cc71d7c2
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical argument.\n", line, name);
bcf3dc79
 				    fclose(fs);
8de0d031
 				    free(name);
 				    free(arg);
81837459
 				    freecfg(copt);
 				    return NULL;
 				}
 				if(regcfg(&copt, name, NULL, atoi(arg), pt->multiple) < 0) {
 				    fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n");
 				    fclose(fs);
8de0d031
 				    free(name);
81837459
 				    free(arg);
8de0d031
 				    freecfg(copt);
e3aaff8e
 				    return NULL;
 				}
2d70a403
 				free(arg);
e3aaff8e
 				break;
 			    case OPT_COMPSIZE:
 				if(!arg) {
81837459
 				    if(verbose)
cc71d7c2
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires argument.\n", line, name);
bcf3dc79
 				    fclose(fs);
8de0d031
 				    free(name);
81837459
 				    freecfg(copt);
e3aaff8e
 				    return NULL;
 				}
 				ctype = tolower(arg[strlen(arg) - 1]);
 				if(ctype == 'm' || ctype == 'k') {
65d08d61
 				    char *cpy = (char *) calloc(strlen(arg), 1);
658f19f8
 				    strncpy(cpy, arg, strlen(arg) - 1);
e3aaff8e
 				    if(!isnumb(cpy)) {
81837459
 					if(verbose)
cc71d7c2
 					    fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical (raw/K/M) argument.\n", line, name);
bcf3dc79
 					fclose(fs);
8de0d031
 					free(name);
 					free(arg);
81837459
 					freecfg(copt);
e3aaff8e
 					return NULL;
 				    }
 				    if(ctype == 'm')
 					calc = atoi(cpy) * 1024 * 1024;
 				    else
 					calc = atoi(cpy) * 1024;
 				    free(cpy);
 				} else {
 				    if(!isnumb(arg)) {
81837459
 					if(verbose)
cc71d7c2
 					    fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical (raw/K/M) argument.\n", line, name);
bcf3dc79
 					fclose(fs);
8de0d031
 					free(name);
 					free(arg);
81837459
 					freecfg(copt);
e3aaff8e
 					return NULL;
 				    }
 				    calc = atoi(arg);
 				}
2d70a403
 				free(arg);
81837459
 				if(regcfg(&copt, name, NULL, calc, pt->multiple) < 0) {
 				    fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n");
bcf3dc79
 				    fclose(fs);
8de0d031
 				    free(name);
 				    free(arg);
81837459
 				    freecfg(copt);
e3aaff8e
 				    return NULL;
 				}
 				break;
81837459
 			    case OPT_BOOL:
 
 				if(!arg) {
 				    if(verbose)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires boolean argument.\n", line, name);
 				    fclose(fs);
8de0d031
 				    free(name);
81837459
 				    freecfg(copt);
 				    return NULL;
 				}
 
 				if(!strcasecmp(arg, "yes") || !strcmp(arg, "1") || !strcasecmp(arg, "true")) {
 				    val = 1;
 				} else if(!strcasecmp(arg, "no") || !strcmp(arg, "0") || !strcasecmp(arg, "false")) {
 				    val = 0;
 				} else {
 				    if(verbose)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires boolean argument.\n", line, name);
 				    fclose(fs);
8de0d031
 				    free(name);
 				    free(arg);
81837459
 				    freecfg(copt);
 				    return NULL;
 				}
 				free(arg);
 				if(regcfg(&copt, name, NULL, val, pt->multiple) < 0) {
 				    fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n");
 				    fclose(fs);
8de0d031
 				    free(name);
 				    free(arg);
81837459
 				    freecfg(copt);
 				    return NULL;
 				}
e3aaff8e
 				break;
2d70a403
 			    default:
81837459
 				if(verbose)
cc71d7c2
 				    fprintf(stderr, "ERROR: Parse error at line %d: Option %s is of unknown type %d\n", line, name, pt->argtype);
8de0d031
 				fclose(fs);
2d70a403
 				free(name);
 				free(arg);
81837459
 				freecfg(copt);
 				return NULL;
e3aaff8e
 			}
 		    }
 		} else
 		    break;
81837459
 	    }
e3aaff8e
 
 	    if(!found) {
81837459
 		if(verbose)
cc71d7c2
 		    fprintf(stderr, "ERROR: Parse error at line %d: Unknown option %s.\n", line, name);
8de0d031
 		free(name);
bcf3dc79
 		fclose(fs);
81837459
 		freecfg(copt);
e3aaff8e
 		return NULL;
 	    }
52c3c225
 	    free(name);
e3aaff8e
 	}
     }
 
     fclose(fs);
     return copt;
 }
 
9e431a95
 void freecfg(struct cfgstruct *copt)
 {
     	struct cfgstruct *handler;
     	struct cfgstruct *arg;
 
81837459
     while(copt) {
9e431a95
 	arg = copt->nextarg;
5467c418
 	while(arg) {
9e431a95
 	    if(arg->strarg) {
 		free(arg->optname);
 		free(arg->strarg);
 		handler = arg;
5467c418
 		arg = arg->nextarg;
9e431a95
 		free(handler);
5467c418
 	    } else
 		arg = arg->nextarg;
9e431a95
 	}
81837459
 	if(copt->optname)
9e431a95
 	    free(copt->optname);
81837459
 
 	if(copt->strarg)
9e431a95
 	    free(copt->strarg);
81837459
 
9e431a95
 	handler = copt;
 	copt = copt->next;
 	free(handler);
     }
     return;
 }
 
1095156a
 const struct cfgstruct *cfgopt(const struct cfgstruct *copt, const char *optname)
 {
     while(copt) {
 	if(copt->optname && !strcmp(copt->optname, optname))
 	    return copt;
 
 	copt = copt->next;
     }
 
     return NULL;
 }
 
 static struct cfgstruct *cfgopt_i(struct cfgstruct *copt, const char *optname)
81837459
 {
797c5b1e
     while(copt) {
 	if(copt->optname && !strcmp(copt->optname, optname))
 	    return copt;
81837459
 
797c5b1e
 	copt = copt->next;
81837459
     }
 
     return NULL;
 }
 
fc83da82
 static int regcfg(struct cfgstruct **copt, const char *optname, char *strarg, int numarg, short multiple)
e3aaff8e
 {
 	struct cfgstruct *newnode, *pt;
 
81837459
 
8ca8a18e
     newnode = (struct cfgstruct *) malloc(sizeof(struct cfgstruct));
81837459
 
     if(!newnode)
 	return -1;
 
52c3c225
     newnode->optname = optname ? strdup(optname) : NULL;
e3aaff8e
     newnode->nextarg = NULL;
     newnode->next = NULL;
81837459
     newnode->enabled = 0;
     newnode->multiple = multiple;
e3aaff8e
 
81837459
     if(strarg) {
e3aaff8e
 	newnode->strarg = strarg;
81837459
 	newnode->enabled = 1;
     } else {
e3aaff8e
 	newnode->strarg = NULL;
     }
 
81837459
     newnode->numarg = numarg;
     if(numarg != -1 && numarg != 0)
 	newnode->enabled = 1;
e3aaff8e
 
1095156a
     if((pt = cfgopt_i(*copt, optname))) {
81837459
 	if(pt->multiple) {
e3aaff8e
 
81837459
 	    if(pt->enabled) {
 		while(pt->nextarg)
 		    pt = pt->nextarg;
e3aaff8e
 
81837459
 		pt->nextarg = newnode;
 	    } else {
52c3c225
 		if(pt->strarg)
 		    free(pt->strarg);
81837459
 		pt->strarg = newnode->strarg;
 		pt->numarg = newnode->numarg;
 		pt->enabled = newnode->enabled;
52c3c225
 		if(newnode->optname)
 		    free(newnode->optname);
81837459
 		free(newnode);
 	    }
 	    return 3; /* registered additional argument */
e3aaff8e
 
81837459
 	} else {
52c3c225
 	    if(pt->strarg)
 		free(pt->strarg);
81837459
 	    pt->strarg = newnode->strarg;
 	    pt->numarg = newnode->numarg;
 	    pt->enabled = newnode->enabled;
52c3c225
 	    if(newnode->optname)
 		free(newnode->optname);
81837459
 	    free(newnode);
 	    return 2;
 	}
e3aaff8e
 
81837459
     } else {
 	newnode->next = *copt;
 	*copt = newnode;
 	return 1;
     }
e3aaff8e
 }
81837459