* file named AUTHORS, in the root directory of this distribution.
*/
/*
- * $Id: util.c,v 1.17 2006/01/14 04:37:19 paddy_s Exp $
+ * $Id: util.c,v 1.42 2006/08/24 01:57:15 paddy_s Exp $
*/
#include "amanda.h"
#include "util.h"
+#include "arglist.h"
+#include "clock.h"
+
+int allow_overwrites;
+int token_pushed;
+
+tok_t tok, pushed_tok;
+val_t tokenval;
+
+int conf_line_num, got_parserror;
+FILE *conf_conf = (FILE *)NULL;
+char *conf_confname = NULL;
+char *conf_line = NULL;
+char *conf_char = NULL;
+
+/*#define NET_READ_DEBUG*/
+
+#ifdef NET_READ_DEBUG
+#define netprintf(x) dbprintf(x)
+#else
+#define netprintf(x)
+#endif
+
+static int make_socket(void);
+static int connect_port(struct sockaddr_in *addrp, in_port_t port, char *proto,
+ struct sockaddr_in *svaddr, int nonblock);
+
+char conftoken_getc(void);
+int conftoken_ungetc(int c);
/*
* Keep calling read() until we've read buflen's worth of data, or EOF,
* Returns the number of bytes read, 0 on EOF, or negative on error.
*/
ssize_t
-fullread(fd, vbuf, buflen)
- int fd;
- void *vbuf;
- size_t buflen;
+fullread(
+ int fd,
+ void * vbuf,
+ size_t buflen)
{
ssize_t nread, tot = 0;
char *buf = vbuf; /* cast to char so we can ++ it */
* Returns the number of bytes written, or negative on error.
*/
ssize_t
-fullwrite(fd, vbuf, buflen)
- int fd;
- const void *vbuf;
- size_t buflen;
+fullwrite(
+ int fd,
+ const void *vbuf,
+ size_t buflen)
{
ssize_t nwritten, tot = 0;
const char *buf = vbuf; /* cast to char so we can ++ it */
return (tot);
}
+static int
+make_socket(void)
+{
+ int s;
+ int save_errno;
+#if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
+ int on=1;
+ int r;
+#endif
+
+ if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+ save_errno = errno;
+ dbprintf(("%s: make_socket: socket() failed: %s\n",
+ debug_prefix(NULL),
+ strerror(save_errno)));
+ errno = save_errno;
+ return -1;
+ }
+ if (s < 0 || s >= (int)FD_SETSIZE) {
+ aclose(s);
+ errno = EMFILE; /* out of range */
+ return -1;
+ }
+
+#ifdef USE_REUSEADDR
+ r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (r < 0) {
+ save_errno = errno;
+ dbprintf(("%s: stream_server: setsockopt(SO_REUSEADDR) failed: %s\n",
+ debug_prefix(NULL),
+ strerror(errno)));
+ errno = save_errno;
+ }
+#endif
+
+#ifdef SO_KEEPALIVE
+ r = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
+ (void *)&on, SIZEOF(on));
+ if (r == -1) {
+ save_errno = errno;
+ dbprintf(("%s: make_socket: setsockopt() failed: %s\n",
+ debug_prefix(NULL),
+ strerror(save_errno)));
+ aclose(s);
+ errno = save_errno;
+ return -1;
+ }
+#endif
+
+ return s;
+}
+
+/* addrp is my address */
+/* svaddr is the address of the remote machine */
+/* return socket on success */
+/* return -1 on failure */
+int
+connect_portrange(
+ struct sockaddr_in *addrp,
+ in_port_t first_port,
+ in_port_t last_port,
+ char * proto,
+ struct sockaddr_in *svaddr,
+ int nonblock)
+{
+ int s;
+ in_port_t port;
+ static in_port_t port_in_use[1024];
+ static int nb_port_in_use = 0;
+ int i;
+
+ assert(first_port <= last_port);
+
+ /* Try a port already used */
+ for(i=0; i < nb_port_in_use; i++) {
+ port = port_in_use[i];
+ if(port >= first_port && port <= last_port) {
+ s = connect_port(addrp, port, proto, svaddr, nonblock);
+ if(s == -2) return -1;
+ if(s > 0) {
+ return s;
+ }
+ }
+ }
+
+ /* Try a port in the range */
+ for (port = first_port; port <= last_port; port++) {
+ s = connect_port(addrp, port, proto, svaddr, nonblock);
+ if(s == -2) return -1;
+ if(s > 0) {
+ port_in_use[nb_port_in_use++] = port;
+ return s;
+ }
+ }
+
+ dbprintf(("%s: connect_portrange: all ports between %d and %d busy\n",
+ debug_prefix_time(NULL),
+ first_port,
+ last_port));
+ errno = EAGAIN;
+ return -1;
+}
+
+/* addrp is my address */
+/* svaddr is the address of the remote machine */
+/* return -2: Don't try again */
+/* return -1: Try with another port */
+/* return >0: this is the connected socket */
+int
+connect_port(
+ struct sockaddr_in *addrp,
+ in_port_t port,
+ char * proto,
+ struct sockaddr_in *svaddr,
+ int nonblock)
+{
+ int save_errno;
+ struct servent * servPort;
+ socklen_t len;
+ int s;
+
+ servPort = getservbyport((int)htons(port), proto);
+ if (servPort != NULL && !strstr(servPort->s_name, "amanda")) {
+ dbprintf(("%s: connect_port: Skip port %d: Owned by %s.\n",
+ debug_prefix_time(NULL), port, servPort->s_name));
+ return -1;
+ }
+
+ if(servPort == NULL)
+ dbprintf(("%s: connect_port: Try port %d: Available - ",
+ debug_prefix_time(NULL), port));
+ else {
+ dbprintf(("%s: connect_port: Try port %d: Owned by %s - ",
+ debug_prefix_time(NULL), port, servPort->s_name));
+ }
+
+ if ((s = make_socket()) == -1) return -2;
+
+ addrp->sin_port = (in_port_t)htons(port);
+
+ if (bind(s, (struct sockaddr *)addrp, sizeof(*addrp)) != 0) {
+ save_errno = errno;
+ aclose(s);
+ if (save_errno != EADDRINUSE) {
+ dbprintf(("errno %d strerror %s\n",
+ errno, strerror(errno)));
+ errno = save_errno;
+ return -2;
+ }
+ errno = save_errno;
+ return -1;
+ }
+
+ /* find out what port was actually used */
+
+ len = sizeof(*addrp);
+ if (getsockname(s, (struct sockaddr *)addrp, &len) == -1) {
+ save_errno = errno;
+ dbprintf(("%s: connect_port: getsockname() failed: %s\n",
+ debug_prefix(NULL),
+ strerror(save_errno)));
+ aclose(s);
+ errno = save_errno;
+ return -1;
+ }
+
+ if (nonblock)
+ fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0)|O_NONBLOCK);
+ if (connect(s, (struct sockaddr *)svaddr,
+ (socklen_t)sizeof(*svaddr)) == -1 && !nonblock) {
+ save_errno = errno;
+ dbprintf(("%s: connect_portrange: connect from %s.%d failed\n",
+ debug_prefix_time(NULL),
+ inet_ntoa(addrp->sin_addr),
+ ntohs(addrp->sin_port),
+ strerror(save_errno)));
+ dbprintf(("%s: connect_portrange: connect to %s.%d failed: %s\n",
+ debug_prefix_time(NULL),
+ inet_ntoa(svaddr->sin_addr),
+ ntohs(svaddr->sin_port),
+ strerror(save_errno)));
+ aclose(s);
+ errno = save_errno;
+ if (save_errno == ECONNREFUSED ||
+ save_errno == ETIMEDOUT) {
+ return -2 ;
+ }
+ return -1;
+ }
+
+ dbprintf(("%s: connected to %s.%d\n",
+ debug_prefix_time(NULL),
+ inet_ntoa(svaddr->sin_addr),
+ ntohs(svaddr->sin_port)));
+ dbprintf(("%s: our side is %s.%d\n",
+ debug_prefix(NULL),
+ inet_ntoa(addrp->sin_addr),
+ ntohs(addrp->sin_port)));
+ return s;
+}
+
+
/*
* Bind to a port in the given range. Takes a begin,end pair of port numbers.
*
* on success.
*/
int
-bind_portrange(s, addrp, first_port, last_port, proto)
- int s;
- struct sockaddr_in *addrp;
- int first_port, last_port;
- char *proto;
+bind_portrange(
+ int s,
+ struct sockaddr_in *addrp,
+ in_port_t first_port,
+ in_port_t last_port,
+ char * proto)
{
- int port, cnt;
- const int num_ports = last_port - first_port + 1;
- int save_errno;
+ in_port_t port;
+ in_port_t cnt;
struct servent *servPort;
+ const in_port_t num_ports = (in_port_t)(last_port - first_port + 1);
- assert(first_port > 0 && first_port <= last_port && last_port < 65536);
+ assert(first_port <= last_port);
/*
* We pick a different starting port based on our pid and the current
* time to avoid always picking the same reserved port twice.
*/
- port = ((getpid() + time(0)) % num_ports) + first_port;
+ port = (in_port_t)(((getpid() + time(0)) % num_ports) + first_port);
+
/*
* Scan through the range, trying all available ports that are either
* not taken in /etc/services or registered for *amanda*. Wrap around
* if we don't happen to start at the beginning.
*/
for (cnt = 0; cnt < num_ports; cnt++) {
- servPort = getservbyport(htons(port), proto);
- if((servPort == NULL) || strstr(servPort->s_name, "amanda")){
- dbprintf(("%s: bind_portrange2: trying port=%d\n",
+ servPort = getservbyport((int)htons(port), proto);
+ if ((servPort == NULL) || strstr(servPort->s_name, "amanda")) {
+ if (servPort == NULL) {
+ dbprintf(("%s: bind_portrange2: Try port %d: Available - ",
debug_prefix_time(NULL), port));
- addrp->sin_port = htons(port);
- if (bind(s, (struct sockaddr *)addrp, sizeof(*addrp)) >= 0)
+ } else {
+ dbprintf(("%s: bind_portrange2: Try port %d: Owned by %s - ",
+ debug_prefix_time(NULL), port, servPort->s_name));
+ }
+ addrp->sin_port = (in_port_t)htons(port);
+ if (bind(s, (struct sockaddr *)addrp, (socklen_t)sizeof(*addrp)) >= 0) {
+ dbprintf(("Success\n"));
return 0;
- /*
- * If the error was something other then port in use, stop.
- */
- if (errno != EADDRINUSE)
- break;
+ }
+ dbprintf(("%s\n", strerror(errno)));
+ } else {
+ dbprintf(("%s: bind_portrange2: Skip port %d: Owned by %s.\n",
+ debug_prefix_time(NULL), port, servPort->s_name));
}
if (++port > last_port)
port = first_port;
}
- if (cnt == num_ports) {
- dbprintf(("%s: bind_portrange: all ports between %d and %d busy\n",
+ dbprintf(("%s: bind_portrange: all ports between %d and %d busy\n",
debug_prefix_time(NULL),
first_port,
last_port));
- errno = EAGAIN;
- } else if (last_port < IPPORT_RESERVED
- && getuid() != 0
- && errno == EACCES) {
- /*
- * Do not bother with an error message in this case because it
- * is expected.
- */
- } else {
- save_errno = errno;
- dbprintf(("%s: bind_portrange: port %d: %s\n",
- debug_prefix_time(NULL),
- port,
- strerror(errno)));
- errno = save_errno;
- }
+ errno = EAGAIN;
return -1;
}
* Construct a datestamp (YYYYMMDD) from a time_t.
*/
char *
-construct_datestamp(t)
- time_t *t;
+construct_datestamp(
+ time_t *t)
{
struct tm *tm;
- char datestamp[3*NUM_STR_SIZE];
+ char datestamp[3 * NUM_STR_SIZE];
time_t when;
- if(t == NULL) {
+ if (t == NULL) {
when = time((time_t *)NULL);
} else {
when = *t;
}
tm = localtime(&when);
- snprintf(datestamp, sizeof(datestamp),
+ if (!tm)
+ return stralloc("19000101");
+
+ snprintf(datestamp, SIZEOF(datestamp),
"%04d%02d%02d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
return stralloc(datestamp);
}
* Construct a timestamp (YYYYMMDDHHMMSS) from a time_t.
*/
char *
-construct_timestamp(t)
- time_t *t;
+construct_timestamp(
+ time_t *t)
{
struct tm *tm;
- char timestamp[6*NUM_STR_SIZE];
+ char timestamp[6 * NUM_STR_SIZE];
time_t when;
- if(t == NULL) {
+ if (t == NULL) {
when = time((time_t *)NULL);
} else {
when = *t;
}
tm = localtime(&when);
- snprintf(timestamp, sizeof(timestamp),
+ if (!tm)
+ return stralloc("19000101000000");
+
+ snprintf(timestamp, SIZEOF(timestamp),
"%04d%02d%02d%02d%02d%02d",
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
return stralloc(timestamp);
}
+
+
+int
+needs_quotes(
+ const char * str)
+{
+ return (match("[ \t\f\r\n\"]", str) != 0);
+}
+
+
+/*
+ * For backward compatibility we are trying for minimal quoting.
+ * We only quote a string if it contains whitespace or is misquoted...
+ */
+
+char *
+quote_string(
+ const char *str)
+{
+ char * s;
+ char * ret;
+
+ if ((str == NULL) || (*str == '\0')) {
+ ret = stralloc("\"\"");
+ } else if ((match("[\\\"[:space:][:cntrl:]]", str)) == 0) {
+ /*
+ * String does not need to be quoted since it contains
+ * neither whitespace, control or quote characters.
+ */
+ ret = stralloc(str);
+ } else {
+ /*
+ * Allocate maximum possible string length.
+ * (a string of all quotes plus room for leading ", trailing " and NULL)
+ */
+ ret = s = alloc((strlen(str) * 2) + 2 + 1);
+ *(s++) = '"';
+ while (*str != '\0') {
+ if (*str == '\t') {
+ *(s++) = '\\';
+ *(s++) = 't';
+ str++;
+ continue;
+ } else if (*str == '\n') {
+ *(s++) = '\\';
+ *(s++) = 'n';
+ str++;
+ continue;
+ } else if (*str == '\r') {
+ *(s++) = '\\';
+ *(s++) = 'r';
+ str++;
+ continue;
+ } else if (*str == '\f') {
+ *(s++) = '\\';
+ *(s++) = 'f';
+ str++;
+ continue;
+ }
+ if (*str == '"')
+ *(s++) = '\\';
+ *(s++) = *(str++);
+ }
+ *(s++) = '"';
+ *s = '\0';
+ }
+ return (ret);
+}
+
+
+char *
+unquote_string(
+ const char *str)
+{
+ char * ret;
+
+ if ((str == NULL) || (*str == '\0')) {
+ ret = stralloc("");
+ } else {
+ char * in;
+ char * out;
+
+ ret = in = out = stralloc(str);
+ while (*in != '\0') {
+ if (*in == '"') {
+ in++;
+ continue;
+ }
+
+ if (*in == '\\') {
+ in++;
+ if (*in == 'n') {
+ in++;
+ *(out++) = '\n';
+ continue;
+ } else if (*in == 't') {
+ in++;
+ *(out++) = '\t';
+ continue;
+ } else if (*in == 'r') {
+ in++;
+ *(out++) = '\r';
+ continue;
+ } else if (*in == 'f') {
+ in++;
+ *(out++) = '\f';
+ continue;
+ }
+ }
+ *(out++) = *(in++);
+ }
+ *out = '\0';
+ }
+ return (ret);
+}
+
+char *
+sanitize_string(
+ const char *str)
+{
+ char * s;
+ char * ret;
+
+ if ((str == NULL) || (*str == '\0')) {
+ ret = stralloc("");
+ } else {
+ ret = stralloc(str);
+ for (s = ret; *s != '\0'; s++) {
+ if (iscntrl(*s))
+ *s = '?';
+ }
+ }
+ return (ret);
+}
+
+char *
+strquotedstr(void)
+{
+ char * tok = strtok(NULL, " ");
+
+ if ((tok != NULL) && (tok[0] == '"')) {
+ char * t;
+ size_t len;
+
+ len = strlen(tok);
+ do {
+ t = strtok(NULL, " ");
+ tok[len] = ' ';
+ len = strlen(tok);
+ } while ((t != NULL) &&
+ (tok[len - 1] != '"') && (tok[len - 2] != '\\'));
+ }
+ return tok;
+}
+
+ssize_t
+hexdump(
+ const char *buffer,
+ size_t len)
+{
+ ssize_t rc = -1;
+
+ FILE *stream = popen("od -w10 -c -x -", "w");
+
+ if (stream != NULL) {
+ fflush(stdout);
+ rc = (ssize_t)fwrite(buffer, len, 1, stream);
+ if (ferror(stream))
+ rc = -1;
+ fclose(stream);
+ }
+ return rc;
+}
+
+/*
+ Return 0 if the following characters are present
+ * ( ) < > [ ] , ; : ! $ \ / "
+ else returns 1
+*/
+
+int
+validate_mailto(
+ const char *mailto)
+{
+ return !match("\\*|<|>|\\(|\\)|\\[|\\]|,|;|:|\\\\|/|\"|\\!|\\$|\\|", mailto);
+}
+
+
+t_conf_var *
+get_np(
+ t_conf_var *get_var,
+ int parm)
+{
+ t_conf_var *np;
+
+ for(np = get_var; np->token != CONF_UNKNOWN; np++) {
+ if(np->parm == parm)
+ break;
+ }
+
+ if(np->token == CONF_UNKNOWN) {
+ error("error [unknown getconf_np parm: %d]", parm);
+ /* NOTREACHED */
+ }
+ return np;
+}
+
+void
+get_simple(
+ val_t *var,
+ tok_t type)
+{
+ ckseen(&var->seen);
+
+ switch(type) {
+ case CONF_STRING:
+ case CONF_IDENT:
+ get_conftoken(type);
+ var->v.s = newstralloc(var->v.s, tokenval.v.s);
+ malloc_mark(var->v.s);
+ break;
+
+ case CONF_INT:
+ var->v.i = get_int();
+ break;
+
+ case CONF_LONG:
+ var->v.l = get_long();
+ break;
+
+ case CONF_SIZE:
+ var->v.size = get_size();
+ break;
+
+ case CONF_AM64:
+ var->v.am64 = get_am64_t();
+ break;
+
+ case CONF_BOOL:
+ var->v.i = get_bool();
+ break;
+
+ case CONF_REAL:
+ get_conftoken(CONF_REAL);
+ var->v.r = tokenval.v.r;
+ break;
+
+ case CONF_TIME:
+ var->v.t = get_time();
+ break;
+
+ default:
+ error("error [unknown get_simple type: %d]", type);
+ /* NOTREACHED */
+ }
+ return;
+}
+
+time_t
+get_time(void)
+{
+ time_t hhmm;
+
+ get_conftoken(CONF_ANY);
+ switch(tok) {
+ case CONF_INT:
+#if SIZEOF_TIME_T < SIZEOF_INT
+ if ((off_t)tokenval.v.i >= (off_t)TIME_MAX)
+ conf_parserror("value too large");
+#endif
+ hhmm = (time_t)tokenval.v.i;
+ break;
+
+ case CONF_LONG:
+#if SIZEOF_TIME_T < SIZEOF_LONG
+ if ((off_t)tokenval.v.l >= (off_t)TIME_MAX)
+ conf_parserror("value too large");
+#endif
+ hhmm = (time_t)tokenval.v.l;
+ break;
+
+ case CONF_SIZE:
+#if SIZEOF_TIME_T < SIZEOF_SSIZE_T
+ if ((off_t)tokenval.v.size >= (off_t)TIME_MAX)
+ conf_parserror("value too large");
+#endif
+ hhmm = (time_t)tokenval.v.size;
+ break;
+
+ case CONF_AM64:
+#if SIZEOF_TIME_T < SIZEOF_LONG_LONG
+ if ((off_t)tokenval.v.am64 >= (off_t)TIME_MAX)
+ conf_parserror("value too large");
+#endif
+ hhmm = (time_t)tokenval.v.am64;
+ break;
+
+ case CONF_AMINFINITY:
+ hhmm = TIME_MAX;
+ break;
+
+ default:
+ conf_parserror("a time is expected");
+ hhmm = 0;
+ break;
+ }
+ return hhmm;
+}
+
+keytab_t numb_keytable[] = {
+ { "B", CONF_MULT1 },
+ { "BPS", CONF_MULT1 },
+ { "BYTE", CONF_MULT1 },
+ { "BYTES", CONF_MULT1 },
+ { "DAY", CONF_MULT1 },
+ { "DAYS", CONF_MULT1 },
+ { "INF", CONF_AMINFINITY },
+ { "K", CONF_MULT1K },
+ { "KB", CONF_MULT1K },
+ { "KBPS", CONF_MULT1K },
+ { "KBYTE", CONF_MULT1K },
+ { "KBYTES", CONF_MULT1K },
+ { "KILOBYTE", CONF_MULT1K },
+ { "KILOBYTES", CONF_MULT1K },
+ { "KPS", CONF_MULT1K },
+ { "M", CONF_MULT1M },
+ { "MB", CONF_MULT1M },
+ { "MBPS", CONF_MULT1M },
+ { "MBYTE", CONF_MULT1M },
+ { "MBYTES", CONF_MULT1M },
+ { "MEG", CONF_MULT1M },
+ { "MEGABYTE", CONF_MULT1M },
+ { "MEGABYTES", CONF_MULT1M },
+ { "G", CONF_MULT1G },
+ { "GB", CONF_MULT1G },
+ { "GBPS", CONF_MULT1G },
+ { "GBYTE", CONF_MULT1G },
+ { "GBYTES", CONF_MULT1G },
+ { "GIG", CONF_MULT1G },
+ { "GIGABYTE", CONF_MULT1G },
+ { "GIGABYTES", CONF_MULT1G },
+ { "MPS", CONF_MULT1M },
+ { "TAPE", CONF_MULT1 },
+ { "TAPES", CONF_MULT1 },
+ { "WEEK", CONF_MULT7 },
+ { "WEEKS", CONF_MULT7 },
+ { NULL, CONF_IDENT }
+};
+
+int
+get_int(void)
+{
+ int val;
+ keytab_t *save_kt;
+
+ save_kt = keytable;
+ keytable = numb_keytable;
+
+ get_conftoken(CONF_ANY);
+ switch(tok) {
+ case CONF_INT:
+ val = tokenval.v.i;
+ break;
+
+ case CONF_LONG:
+#if SIZEOF_INT < SIZEOF_LONG
+ if ((off_t)tokenval.v.l > (off_t)INT_MAX)
+ conf_parserror("value too large");
+ if ((off_t)tokenval.v.l < (off_t)INT_MIN)
+ conf_parserror("value too small");
+#endif
+ val = (int)tokenval.v.l;
+ break;
+
+ case CONF_SIZE:
+#if SIZEOF_INT < SIZEOF_SSIZE_T
+ if ((off_t)tokenval.v.size > (off_t)INT_MAX)
+ conf_parserror("value too large");
+ if ((off_t)tokenval.v.size < (off_t)INT_MIN)
+ conf_parserror("value too small");
+#endif
+ val = (int)tokenval.v.size;
+ break;
+
+ case CONF_AM64:
+#if SIZEOF_INT < SIZEOF_LONG_LONG
+ if (tokenval.v.am64 > (off_t)INT_MAX)
+ conf_parserror("value too large");
+ if (tokenval.v.am64 < (off_t)INT_MIN)
+ conf_parserror("value too small");
+#endif
+ val = (int)tokenval.v.am64;
+ break;
+
+ case CONF_AMINFINITY:
+ val = INT_MAX;
+ break;
+
+ default:
+ conf_parserror("an int is expected");
+ val = 0;
+ break;
+ }
+
+ /* get multiplier, if any */
+ get_conftoken(CONF_ANY);
+ switch(tok) {
+ case CONF_NL: /* multiply by one */
+ case CONF_END:
+ case CONF_MULT1:
+ case CONF_MULT1K:
+ break;
+
+ case CONF_MULT7:
+ if (val > (INT_MAX / 7))
+ conf_parserror("value too large");
+ if (val < (INT_MIN / 7))
+ conf_parserror("value too small");
+ val *= 7;
+ break;
+
+ case CONF_MULT1M:
+ if (val > (INT_MAX / 1024))
+ conf_parserror("value too large");
+ if (val < (INT_MIN / 1024))
+ conf_parserror("value too small");
+ val *= 1024;
+ break;
+
+ case CONF_MULT1G:
+ if (val > (INT_MAX / (1024 * 1024)))
+ conf_parserror("value too large");
+ if (val < (INT_MIN / (1024 * 1024)))
+ conf_parserror("value too small");
+ val *= 1024 * 1024;
+ break;
+
+ default: /* it was not a multiplier */
+ unget_conftoken();
+ break;
+ }
+
+ keytable = save_kt;
+ return val;
+}
+
+long
+get_long(void)
+{
+ long val;
+ keytab_t *save_kt;
+
+ save_kt = keytable;
+ keytable = numb_keytable;
+
+ get_conftoken(CONF_ANY);
+
+ switch(tok) {
+ case CONF_LONG:
+ val = tokenval.v.l;
+ break;
+
+ case CONF_INT:
+#if SIZEOF_LONG < SIZEOF_INT
+ if ((off_t)tokenval.v.i > (off_t)LONG_MAX)
+ conf_parserror("value too large");
+ if ((off_t)tokenval.v.i < (off_t)LONG_MIN)
+ conf_parserror("value too small");
+#endif
+ val = (long)tokenval.v.i;
+ break;
+
+ case CONF_SIZE:
+#if SIZEOF_LONG < SIZEOF_SSIZE_T
+ if ((off_t)tokenval.v.size > (off_t)LONG_MAX)
+ conf_parserror("value too large");
+ if ((off_t)tokenval.v.size < (off_t)LONG_MIN)
+ conf_parserror("value too small");
+#endif
+ val = (long)tokenval.v.size;
+ break;
+
+ case CONF_AM64:
+#if SIZEOF_LONG < SIZEOF_LONG_LONG
+ if (tokenval.v.am64 > (off_t)LONG_MAX)
+ conf_parserror("value too large");
+ if (tokenval.v.am64 < (off_t)LONG_MIN)
+ conf_parserror("value too small");
+#endif
+ val = (long)tokenval.v.am64;
+ break;
+
+ case CONF_AMINFINITY:
+ val = (long)LONG_MAX;
+ break;
+
+ default:
+ conf_parserror("a long is expected");
+ val = 0;
+ break;
+ }
+
+ /* get multiplier, if any */
+ get_conftoken(CONF_ANY);
+
+ switch(tok) {
+ case CONF_NL: /* multiply by one */
+ case CONF_MULT1:
+ case CONF_MULT1K:
+ break;
+
+ case CONF_MULT7:
+ if (val > (LONG_MAX / 7L))
+ conf_parserror("value too large");
+ if (val < (LONG_MIN / 7L))
+ conf_parserror("value too small");
+ val *= 7L;
+ break;
+
+ case CONF_MULT1M:
+ if (val > (LONG_MAX / 1024L))
+ conf_parserror("value too large");
+ if (val < (LONG_MIN / 1024L))
+ conf_parserror("value too small");
+ val *= 1024L;
+ break;
+
+ case CONF_MULT1G:
+ if (val > (LONG_MAX / (1024L * 1024L)))
+ conf_parserror("value too large");
+ if (val < (LONG_MIN / (1024L * 1024L)))
+ conf_parserror("value too small");
+ val *= 1024L * 1024L;
+ break;
+
+ default: /* it was not a multiplier */
+ unget_conftoken();
+ break;
+ }
+
+ keytable = save_kt;
+ return val;
+}
+
+ssize_t
+get_size(void)
+{
+ ssize_t val;
+ keytab_t *save_kt;
+
+ save_kt = keytable;
+ keytable = numb_keytable;
+
+ get_conftoken(CONF_ANY);
+
+ switch(tok) {
+ case CONF_SIZE:
+ val = tokenval.v.size;
+ break;
+
+ case CONF_INT:
+#if SIZEOF_SIZE_T < SIZEOF_INT
+ if ((off_t)tokenval.v.i > (off_t)SSIZE_MAX)
+ conf_parserror("value too large");
+ if ((off_t)tokenval.v.i < (off_t)SSIZE_MIN)
+ conf_parserror("value too small");
+#endif
+ val = (ssize_t)tokenval.v.i;
+ break;
+
+ case CONF_LONG:
+#if SIZEOF_SIZE_T < SIZEOF_LONG
+ if ((off_t)tokenval.v.l > (off_t)SSIZE_MAX)
+ conf_parserror("value too large");
+ if ((off_t)tokenval.v.l < (off_t)SSIZE_MIN)
+ conf_parserror("value too small");
+#endif
+ val = (ssize_t)tokenval.v.l;
+ break;
+
+ case CONF_AM64:
+#if SIZEOF_SIZE_T < SIZEOF_LONG_LONG
+ if (tokenval.v.am64 > (off_t)SSIZE_MAX)
+ conf_parserror("value too large");
+ if (tokenval.v.am64 < (off_t)SSIZE_MIN)
+ conf_parserror("value too small");
+#endif
+ val = (ssize_t)tokenval.v.am64;
+ break;
+
+ case CONF_AMINFINITY:
+ val = (ssize_t)SSIZE_MAX;
+ break;
+
+ default:
+ conf_parserror("an integer is expected");
+ val = 0;
+ break;
+ }
+
+ /* get multiplier, if any */
+ get_conftoken(CONF_ANY);
+
+ switch(tok) {
+ case CONF_NL: /* multiply by one */
+ case CONF_MULT1:
+ case CONF_MULT1K:
+ break;
+
+ case CONF_MULT7:
+ if (val > (ssize_t)(SSIZE_MAX / 7))
+ conf_parserror("value too large");
+ if (val < (ssize_t)(SSIZE_MIN / 7))
+ conf_parserror("value too small");
+ val *= (ssize_t)7;
+ break;
+
+ case CONF_MULT1M:
+ if (val > (ssize_t)(SSIZE_MAX / (ssize_t)1024))
+ conf_parserror("value too large");
+ if (val < (ssize_t)(SSIZE_MIN / (ssize_t)1024))
+ conf_parserror("value too small");
+ val *= (ssize_t)1024;
+ break;
+
+ case CONF_MULT1G:
+ if (val > (ssize_t)(SSIZE_MAX / (1024 * 1024)))
+ conf_parserror("value too large");
+ if (val < (ssize_t)(SSIZE_MIN / (1024 * 1024)))
+ conf_parserror("value too small");
+ val *= (ssize_t)(1024 * 1024);
+ break;
+
+ default: /* it was not a multiplier */
+ unget_conftoken();
+ break;
+ }
+
+ keytable = save_kt;
+ return val;
+}
+
+off_t
+get_am64_t(void)
+{
+ off_t val;
+ keytab_t *save_kt;
+
+ save_kt = keytable;
+ keytable = numb_keytable;
+
+ get_conftoken(CONF_ANY);
+
+ switch(tok) {
+ case CONF_INT:
+ val = (off_t)tokenval.v.i;
+ break;
+
+ case CONF_LONG:
+ val = (off_t)tokenval.v.l;
+ break;
+
+ case CONF_SIZE:
+ val = (off_t)tokenval.v.size;
+ break;
+
+ case CONF_AM64:
+ val = tokenval.v.am64;
+ break;
+
+ case CONF_AMINFINITY:
+ val = AM64_MAX;
+ break;
+
+ default:
+ conf_parserror("an am64 is expected %d", tok);
+ val = 0;
+ break;
+ }
+
+ /* get multiplier, if any */
+ get_conftoken(CONF_ANY);
+
+ switch(tok) {
+ case CONF_NL: /* multiply by one */
+ case CONF_MULT1:
+ case CONF_MULT1K:
+ break;
+
+ case CONF_MULT7:
+ if (val > AM64_MAX/7 || val < AM64_MIN/7)
+ conf_parserror("value too large");
+ val *= 7;
+ break;
+
+ case CONF_MULT1M:
+ if (val > AM64_MAX/1024 || val < AM64_MIN/1024)
+ conf_parserror("value too large");
+ val *= 1024;
+ break;
+
+ case CONF_MULT1G:
+ if (val > AM64_MAX/(1024*1024) || val < AM64_MIN/(1024*1024))
+ conf_parserror("value too large");
+ val *= 1024*1024;
+ break;
+
+ default: /* it was not a multiplier */
+ unget_conftoken();
+ break;
+ }
+
+ keytable = save_kt;
+
+ return val;
+}
+
+keytab_t bool_keytable[] = {
+ { "Y", CONF_ATRUE },
+ { "YES", CONF_ATRUE },
+ { "T", CONF_ATRUE },
+ { "TRUE", CONF_ATRUE },
+ { "ON", CONF_ATRUE },
+ { "N", CONF_AFALSE },
+ { "NO", CONF_AFALSE },
+ { "F", CONF_AFALSE },
+ { "FALSE", CONF_AFALSE },
+ { "OFF", CONF_AFALSE },
+ { NULL, CONF_IDENT }
+};
+
+int
+get_bool(void)
+{
+ int val;
+ keytab_t *save_kt;
+
+ save_kt = keytable;
+ keytable = bool_keytable;
+
+ get_conftoken(CONF_ANY);
+
+ switch(tok) {
+ case CONF_INT:
+ if (tokenval.v.i != 0)
+ val = 1;
+ else
+ val = 0;
+ break;
+
+ case CONF_LONG:
+ if (tokenval.v.l != 0L)
+ val = 1;
+ else
+ val = 0;
+ break;
+
+ case CONF_SIZE:
+ if (tokenval.v.size != (size_t)0)
+ val = 1;
+ else
+ val = 0;
+ break;
+
+ case CONF_AM64:
+ if (tokenval.v.am64 != (off_t)0)
+ val = 1;
+ else
+ val = 0;
+ break;
+
+ case CONF_ATRUE:
+ val = 1;
+ break;
+
+ case CONF_AFALSE:
+ val = 0;
+ break;
+
+ case CONF_NL:
+ unget_conftoken();
+ val = 2; /* no argument - most likely TRUE */
+ break;
+ default:
+ unget_conftoken();
+ val = 3; /* a bad argument - most likely TRUE */
+ conf_parserror("YES, NO, TRUE, FALSE, ON, OFF expected");
+ break;
+ }
+
+ keytable = save_kt;
+ return val;
+}
+
+void
+ckseen(
+ int *seen)
+{
+ if (*seen && !allow_overwrites && conf_line_num != -2) {
+ conf_parserror("duplicate parameter, prev def on line %d", *seen);
+ }
+ *seen = conf_line_num;
+}
+
+printf_arglist_function(void conf_parserror, const char *, format)
+{
+ va_list argp;
+
+ /* print error message */
+
+ if(conf_line)
+ fprintf(stderr, "argument \"%s\": ", conf_line);
+ else
+ fprintf(stderr, "\"%s\", line %d: ", conf_confname, conf_line_num);
+ arglist_start(argp, format);
+ vfprintf(stderr, format, argp);
+ arglist_end(argp);
+ fputc('\n', stderr);
+
+ got_parserror = 1;
+}
+
+tok_t
+lookup_keyword(
+ char * str)
+{
+ keytab_t *kwp;
+
+ /* switch to binary search if performance warrants */
+
+ for(kwp = keytable; kwp->keyword != NULL; kwp++) {
+ if (strcmp(kwp->keyword, str) == 0) break;
+ }
+ return kwp->token;
+}
+
+char tkbuf[4096];
+
+/* push the last token back (can only unget ANY tokens) */
+void
+unget_conftoken(void)
+{
+ token_pushed = 1;
+ pushed_tok = tok;
+ tok = CONF_UNKNOWN;
+ return;
+}
+
+char
+conftoken_getc(void)
+{
+ if(conf_line == NULL)
+ return getc(conf_conf);
+ if(*conf_char == '\0')
+ return -1;
+ return(*conf_char++);
+}
+
+int
+conftoken_ungetc(
+ int c)
+{
+ if(conf_line == NULL)
+ return ungetc(c, conf_conf);
+ else if(conf_char > conf_line) {
+ if(c == -1)
+ return c;
+ conf_char--;
+ if(*conf_char != c) {
+ error("*conf_char != c : %c %c", *conf_char, c);
+ /* NOTREACHED */
+ }
+ } else {
+ error("conf_char == conf_line");
+ /* NOTREACHED */
+ }
+ return c;
+}
+
+void
+get_conftoken(
+ tok_t exp)
+{
+ int ch, d;
+ off_t am64;
+ char *buf;
+ char *tmps;
+ int token_overflow;
+ int inquote = 0;
+ int escape = 0;
+ int sign;
+
+ if (token_pushed) {
+ token_pushed = 0;
+ tok = pushed_tok;
+
+ /*
+ ** If it looked like a key word before then look it
+ ** up again in the current keyword table.
+ */
+ switch(tok) {
+ case CONF_LONG: case CONF_AM64: case CONF_SIZE:
+ case CONF_INT: case CONF_REAL: case CONF_STRING:
+ case CONF_LBRACE: case CONF_RBRACE: case CONF_COMMA:
+ case CONF_NL: case CONF_END: case CONF_UNKNOWN:
+ case CONF_TIME:
+ break;
+
+ default:
+ if (exp == CONF_IDENT)
+ tok = CONF_IDENT;
+ else
+ tok = lookup_keyword(tokenval.v.s);
+ break;
+ }
+ }
+ else {
+ ch = conftoken_getc();
+
+ while(ch != EOF && ch != '\n' && isspace(ch))
+ ch = conftoken_getc();
+ if (ch == '#') { /* comment - eat everything but eol/eof */
+ while((ch = conftoken_getc()) != EOF && ch != '\n') {
+ (void)ch; /* Quiet empty loop complaints */
+ }
+ }
+
+ if (isalpha(ch)) { /* identifier */
+ buf = tkbuf;
+ token_overflow = 0;
+ do {
+ if (islower(ch)) ch = toupper(ch);
+ if (buf < tkbuf+sizeof(tkbuf)-1) {
+ *buf++ = (char)ch;
+ } else {
+ *buf = '\0';
+ if (!token_overflow) {
+ conf_parserror("token too long: %.20s...", tkbuf);
+ }
+ token_overflow = 1;
+ }
+ ch = conftoken_getc();
+ } while(isalnum(ch) || ch == '_' || ch == '-');
+
+ if (ch != EOF && conftoken_ungetc(ch) == EOF) {
+ if (ferror(conf_conf)) {
+ conf_parserror("Pushback of '%c' failed: %s",
+ ch, strerror(ferror(conf_conf)));
+ } else {
+ conf_parserror("Pushback of '%c' failed: EOF", ch);
+ }
+ }
+ *buf = '\0';
+
+ tokenval.v.s = tkbuf;
+
+ if (token_overflow) tok = CONF_UNKNOWN;
+ else if (exp == CONF_IDENT) tok = CONF_IDENT;
+ else tok = lookup_keyword(tokenval.v.s);
+ }
+ else if (isdigit(ch)) { /* integer */
+ sign = 1;
+
+negative_number: /* look for goto negative_number below sign is set there */
+ am64 = 0;
+ do {
+ am64 = am64 * 10 + (ch - '0');
+ ch = conftoken_getc();
+ } while (isdigit(ch));
+
+ if (ch != '.') {
+ if (exp == CONF_INT) {
+ tok = CONF_INT;
+ tokenval.v.i = sign * (int)am64;
+ } else if (exp == CONF_LONG) {
+ tok = CONF_LONG;
+ tokenval.v.l = (long)sign * (long)am64;
+ } else if (exp != CONF_REAL) {
+ tok = CONF_AM64;
+ tokenval.v.am64 = (off_t)sign * am64;
+ } else {
+ /* automatically convert to real when expected */
+ tokenval.v.r = (double)sign * (double)am64;
+ tok = CONF_REAL;
+ }
+ } else {
+ /* got a real number, not an int */
+ tokenval.v.r = sign * (double) am64;
+ am64 = 0;
+ d = 1;
+ ch = conftoken_getc();
+ while (isdigit(ch)) {
+ am64 = am64 * 10 + (ch - '0');
+ d = d * 10;
+ ch = conftoken_getc();
+ }
+ tokenval.v.r += sign * ((double)am64) / d;
+ tok = CONF_REAL;
+ }
+
+ if (ch != EOF && conftoken_ungetc(ch) == EOF) {
+ if (ferror(conf_conf)) {
+ conf_parserror("Pushback of '%c' failed: %s",
+ ch, strerror(ferror(conf_conf)));
+ } else {
+ conf_parserror("Pushback of '%c' failed: EOF", ch);
+ }
+ }
+ } else switch(ch) {
+ case '"': /* string */
+ buf = tkbuf;
+ token_overflow = 0;
+ inquote = 1;
+ *buf++ = (char)ch;
+ while (inquote && ((ch = conftoken_getc()) != EOF)) {
+ if (ch == '\n') {
+ if (!escape)
+ break;
+ escape = 0;
+ buf--; /* Consume escape in buffer */
+ } else if (ch == '\\') {
+ escape = 1;
+ } else {
+ if (ch == '"') {
+ if (!escape)
+ inquote = 0;
+ }
+ escape = 0;
+ }
+
+ if(buf >= &tkbuf[sizeof(tkbuf) - 1]) {
+ if (!token_overflow) {
+ conf_parserror("string too long: %.20s...", tkbuf);
+ }
+ token_overflow = 1;
+ break;
+ }
+ *buf++ = (char)ch;
+ }
+ *buf = '\0';
+
+ /*
+ * A little manuver to leave a fully unquoted, unallocated string
+ * in tokenval.v.s
+ */
+ tmps = unquote_string(tkbuf);
+ strncpy(tkbuf, tmps, sizeof(tkbuf));
+ amfree(tmps);
+ tokenval.v.s = tkbuf;
+
+ tok = (token_overflow) ? CONF_UNKNOWN :
+ (exp == CONF_IDENT) ? CONF_IDENT : CONF_STRING;
+ break;
+
+ case '-':
+ ch = conftoken_getc();
+ if (isdigit(ch)) {
+ sign = -1;
+ goto negative_number;
+ }
+ else {
+ if (ch != EOF && conftoken_ungetc(ch) == EOF) {
+ if (ferror(conf_conf)) {
+ conf_parserror("Pushback of '%c' failed: %s",
+ ch, strerror(ferror(conf_conf)));
+ } else {
+ conf_parserror("Pushback of '%c' failed: EOF", ch);
+ }
+ }
+ tok = CONF_UNKNOWN;
+ }
+ break;
+
+ case ',':
+ tok = CONF_COMMA;
+ break;
+
+ case '{':
+ tok = CONF_LBRACE;
+ break;
+
+ case '}':
+ tok = CONF_RBRACE;
+ break;
+
+ case '\n':
+ tok = CONF_NL;
+ break;
+
+ case EOF:
+ tok = CONF_END;
+ break;
+
+ default:
+ tok = CONF_UNKNOWN;
+ break;
+ }
+ }
+
+ if (exp != CONF_ANY && tok != exp) {
+ char *str;
+ keytab_t *kwp;
+
+ switch(exp) {
+ case CONF_LBRACE:
+ str = "\"{\"";
+ break;
+
+ case CONF_RBRACE:
+ str = "\"}\"";
+ break;
+
+ case CONF_COMMA:
+ str = "\",\"";
+ break;
+
+ case CONF_NL:
+ str = "end of line";
+ break;
+
+ case CONF_END:
+ str = "end of file";
+ break;
+
+ case CONF_INT:
+ str = "an integer";
+ break;
+
+ case CONF_REAL:
+ str = "a real number";
+ break;
+
+ case CONF_STRING:
+ str = "a quoted string";
+ break;
+
+ case CONF_IDENT:
+ str = "an identifier";
+ break;
+
+ default:
+ for(kwp = keytable; kwp->keyword != NULL; kwp++) {
+ if (exp == kwp->token)
+ break;
+ }
+ if (kwp->keyword == NULL)
+ str = "token not";
+ else
+ str = kwp->keyword;
+ break;
+ }
+ conf_parserror("%s is expected", str);
+ tok = exp;
+ if (tok == CONF_INT)
+ tokenval.v.i = 0;
+ else
+ tokenval.v.s = "";
+ }
+}
+
+
+void
+read_string(
+ t_conf_var *np,
+ val_t *val)
+{
+ np = np;
+ ckseen(&val->seen);
+ get_conftoken(CONF_STRING);
+ val->v.s = newstralloc(val->v.s, tokenval.v.s);
+}
+
+void
+read_ident(
+ t_conf_var *np,
+ val_t *val)
+{
+ np = np;
+ ckseen(&val->seen);
+ get_conftoken(CONF_IDENT);
+ val->v.s = newstralloc(val->v.s, tokenval.v.s);
+}
+
+void
+read_int(
+ t_conf_var *np,
+ val_t *val)
+{
+ np = np;
+ ckseen(&val->seen);
+ val->v.i = get_int();
+}
+
+void
+read_long(
+ t_conf_var *np,
+ val_t *val)
+{
+ np = np;
+ ckseen(&val->seen);
+ val->v.l = get_long();
+}
+
+void
+read_size(
+ t_conf_var *np,
+ val_t *val)
+{
+ np = np;
+ ckseen(&val->seen);
+ val->v.size = get_size();
+}
+
+void
+read_am64(
+ t_conf_var *np,
+ val_t *val)
+{
+ np = np;
+ ckseen(&val->seen);
+ val->v.am64 = get_am64_t();
+}
+
+void
+read_bool(
+ t_conf_var *np,
+ val_t *val)
+{
+ np = np;
+ ckseen(&val->seen);
+ val->v.i = get_bool();
+}
+
+void
+read_real(
+ t_conf_var *np,
+ val_t *val)
+{
+ np = np;
+ ckseen(&val->seen);
+ get_conftoken(CONF_REAL);
+ val->v.r = tokenval.v.r;
+}
+
+void
+read_time(
+ t_conf_var *np,
+ val_t *val)
+{
+ np = np;
+ ckseen(&val->seen);
+ val->v.t = get_time();
+}
+
+void
+copy_val_t(
+ val_t *valdst,
+ val_t *valsrc)
+{
+ if(valsrc->seen) {
+ valdst->type = valsrc->type;
+ valdst->seen = valsrc->seen;
+ switch(valsrc->type) {
+ case CONFTYPE_INT:
+ case CONFTYPE_BOOL:
+ case CONFTYPE_COMPRESS:
+ case CONFTYPE_ENCRYPT:
+ case CONFTYPE_HOLDING:
+ case CONFTYPE_ESTIMATE:
+ case CONFTYPE_STRATEGY:
+ case CONFTYPE_TAPERALGO:
+ case CONFTYPE_PRIORITY:
+ valdst->v.i = valsrc->v.i;
+ break;
+
+ case CONFTYPE_LONG:
+ valdst->v.l = valsrc->v.l;
+ break;
+
+ case CONFTYPE_SIZE:
+ valdst->v.size = valsrc->v.size;
+ break;
+
+ case CONFTYPE_AM64:
+ valdst->v.am64 = valsrc->v.am64;
+ break;
+
+ case CONFTYPE_REAL:
+ valdst->v.r = valsrc->v.r;
+ break;
+
+ case CONFTYPE_RATE:
+ valdst->v.rate[0] = valsrc->v.rate[0];
+ valdst->v.rate[1] = valsrc->v.rate[1];
+ break;
+
+ case CONFTYPE_IDENT:
+ case CONFTYPE_STRING:
+ valdst->v.s = stralloc(valsrc->v.s);
+ break;
+
+ case CONFTYPE_TIME:
+ valdst->v.t = valsrc->v.t;
+ break;
+
+ case CONFTYPE_SL:
+ valdst->v.sl = duplicate_sl(valsrc->v.sl);
+ break;
+
+ case CONFTYPE_EXINCLUDE:
+ valdst->v.exinclude.type = valsrc->v.exinclude.type;
+ valdst->v.exinclude.optional = valsrc->v.exinclude.optional;
+ valdst->v.exinclude.sl = duplicate_sl(valsrc->v.exinclude.sl);
+ break;
+ }
+ }
+}
+
+void
+free_val_t(
+ val_t *val)
+{
+ switch(val->type) {
+ case CONFTYPE_INT:
+ case CONFTYPE_BOOL:
+ case CONFTYPE_COMPRESS:
+ case CONFTYPE_ENCRYPT:
+ case CONFTYPE_HOLDING:
+ case CONFTYPE_ESTIMATE:
+ case CONFTYPE_STRATEGY:
+ case CONFTYPE_SIZE:
+ case CONFTYPE_TAPERALGO:
+ case CONFTYPE_PRIORITY:
+ case CONFTYPE_LONG:
+ case CONFTYPE_AM64:
+ case CONFTYPE_REAL:
+ case CONFTYPE_RATE:
+ break;
+
+ case CONFTYPE_IDENT:
+ case CONFTYPE_STRING:
+ amfree(val->v.s);
+ break;
+
+ case CONFTYPE_TIME:
+ break;
+
+ case CONFTYPE_SL:
+ free_sl(val->v.sl);
+ break;
+
+ case CONFTYPE_EXINCLUDE:
+ free_sl(val->v.exinclude.sl);
+ break;
+ }
+ val->seen = 0;
+}
+
+char *
+taperalgo2str(
+ int taperalgo)
+{
+ if(taperalgo == ALGO_FIRST) return "FIRST";
+ if(taperalgo == ALGO_FIRSTFIT) return "FIRSTFIT";
+ if(taperalgo == ALGO_LARGEST) return "LARGEST";
+ if(taperalgo == ALGO_LARGESTFIT) return "LARGESTFIT";
+ if(taperalgo == ALGO_SMALLEST) return "SMALLEST";
+ if(taperalgo == ALGO_LAST) return "LAST";
+ return "UNKNOWN";
+}
+
+static char buffer_conf_print[1025];
+
+char *
+conf_print(
+ val_t *val)
+{
+ struct tm *stm;
+ int pos;
+
+ buffer_conf_print[0] = '\0';
+ switch(val->type) {
+ case CONFTYPE_INT:
+ snprintf(buffer_conf_print, SIZEOF(buffer_conf_print), "%d", val->v.i);
+ break;
+
+ case CONFTYPE_LONG:
+ snprintf(buffer_conf_print, SIZEOF(buffer_conf_print), "%ld", val->v.l);
+ break;
+
+ case CONFTYPE_SIZE:
+ snprintf(buffer_conf_print, SIZEOF(buffer_conf_print), SSIZE_T_FMT,
+ (SSIZE_T_FMT_TYPE)val->v.size);
+ break;
+
+ case CONFTYPE_AM64:
+ snprintf(buffer_conf_print, SIZEOF(buffer_conf_print), OFF_T_FMT ,
+ (OFF_T_FMT_TYPE)val->v.am64);
+ break;
+
+ case CONFTYPE_REAL:
+ snprintf(buffer_conf_print, SIZEOF(buffer_conf_print), "%0.5f" , val->v.r);
+ break;
+
+ case CONFTYPE_RATE:
+ snprintf(buffer_conf_print, SIZEOF(buffer_conf_print), "%0.5f %0.5f" , val->v.rate[0], val->v.rate[1]);
+ break;
+
+ case CONFTYPE_IDENT:
+ if(val->v.s) {
+ strncpy(buffer_conf_print, val->v.s, SIZEOF(buffer_conf_print));
+ buffer_conf_print[SIZEOF(buffer_conf_print) - 1] = '\0';
+ } else
+ buffer_conf_print[0] = '\0';
+ break;
+
+ case CONFTYPE_STRING:
+ buffer_conf_print[0] = '"';
+ if(val->v.s) {
+ strncpy(&buffer_conf_print[1], val->v.s,
+ SIZEOF(buffer_conf_print) - 1);
+ buffer_conf_print[SIZEOF(buffer_conf_print) - 2] = '\0';
+ buffer_conf_print[strlen(buffer_conf_print)] = '"';
+ } else {
+ buffer_conf_print[1] = '"';
+ buffer_conf_print[2] = '\0';
+ }
+ break;
+
+ case CONFTYPE_TIME:
+ stm = localtime(&val->v.t);
+ if (stm) {
+ snprintf(buffer_conf_print, SIZEOF(buffer_conf_print),
+ "%d%02d%02d", stm->tm_hour, stm->tm_min, stm->tm_sec);
+ } else {
+ strcpy(buffer_conf_print, "00000");
+ }
+ break;
+
+ case CONFTYPE_SL:
+ buffer_conf_print[0] = '\0';
+ break;
+
+ case CONFTYPE_EXINCLUDE:
+ buffer_conf_print[0] = '\0';
+ if(val->v.exinclude.type == 0)
+ strncpy(buffer_conf_print, "LIST ", SIZEOF(buffer_conf_print));
+ else
+ strncpy(buffer_conf_print, "FILE ", SIZEOF(buffer_conf_print));
+ pos = 5;
+ if(val->v.exinclude.optional == 1)
+ strncpy(&buffer_conf_print[pos], "OPTIONAL ", SIZEOF(buffer_conf_print));
+ pos += 9;
+ break;
+
+ case CONFTYPE_BOOL:
+ if(val->v.i)
+ strncpy(buffer_conf_print, "yes", SIZEOF(buffer_conf_print));
+ else
+ strncpy(buffer_conf_print, "no", SIZEOF(buffer_conf_print));
+ break;
+
+ case CONFTYPE_STRATEGY:
+ switch(val->v.i) {
+ case DS_SKIP:
+ strncpy(buffer_conf_print, "SKIP", SIZEOF(buffer_conf_print));
+ break;
+
+ case DS_STANDARD:
+ strncpy(buffer_conf_print, "STANDARD", SIZEOF(buffer_conf_print));
+ break;
+
+ case DS_NOFULL:
+ strncpy(buffer_conf_print, "NOFULL", SIZEOF(buffer_conf_print));
+ break;
+
+ case DS_NOINC:
+ strncpy(buffer_conf_print, "NOINC", SIZEOF(buffer_conf_print));
+ break;
+
+ case DS_HANOI:
+ strncpy(buffer_conf_print, "HANOI", SIZEOF(buffer_conf_print));
+ break;
+
+ case DS_INCRONLY:
+ strncpy(buffer_conf_print, "INCRONLY", SIZEOF(buffer_conf_print));
+ break;
+ }
+ break;
+
+ case CONFTYPE_COMPRESS:
+ switch(val->v.i) {
+ case COMP_NONE:
+ strncpy(buffer_conf_print, "NONE", SIZEOF(buffer_conf_print));
+ break;
+
+ case COMP_FAST:
+ strncpy(buffer_conf_print, "CLIENT FAST", SIZEOF(buffer_conf_print));
+ break;
+
+ case COMP_BEST:
+ strncpy(buffer_conf_print, "CLIENT BEST", SIZEOF(buffer_conf_print));
+ break;
+
+ case COMP_CUST:
+ strncpy(buffer_conf_print, "CLIENT CUSTOM", SIZEOF(buffer_conf_print));
+ break;
+
+ case COMP_SERV_FAST:
+ strncpy(buffer_conf_print, "SERVER FAST", SIZEOF(buffer_conf_print));
+ break;
+
+ case COMP_SERV_BEST:
+ strncpy(buffer_conf_print, "SERVER FAST", SIZEOF(buffer_conf_print));
+ break;
+
+ case COMP_SERV_CUST:
+ strncpy(buffer_conf_print, "SERVER CUSTOM", SIZEOF(buffer_conf_print));
+ break;
+ }
+ break;
+
+ case CONFTYPE_ESTIMATE:
+ switch(val->v.i) {
+ case ES_CLIENT:
+ strncpy(buffer_conf_print, "CLIENT", SIZEOF(buffer_conf_print));
+ break;
+
+ case ES_SERVER:
+ strncpy(buffer_conf_print, "SERVER", SIZEOF(buffer_conf_print));
+ break;
+
+ case ES_CALCSIZE:
+ strncpy(buffer_conf_print, "CALCSIZE", SIZEOF(buffer_conf_print));
+ break;
+ }
+ break;
+
+ case CONFTYPE_ENCRYPT:
+ switch(val->v.i) {
+ case ENCRYPT_NONE:
+ strncpy(buffer_conf_print, "NONE", SIZEOF(buffer_conf_print));
+ break;
+
+ case ENCRYPT_CUST:
+ strncpy(buffer_conf_print, "CLIENT", SIZEOF(buffer_conf_print));
+ break;
+
+ case ENCRYPT_SERV_CUST:
+ strncpy(buffer_conf_print, "SERVER", SIZEOF(buffer_conf_print));
+ break;
+ }
+ break;
+
+ case CONFTYPE_HOLDING:
+ switch(val->v.i) {
+ case HOLD_NEVER:
+ strncpy(buffer_conf_print, "NEVER", SIZEOF(buffer_conf_print));
+ break;
+
+ case HOLD_AUTO:
+ strncpy(buffer_conf_print, "AUTO", SIZEOF(buffer_conf_print));
+ break;
+
+ case HOLD_REQUIRED:
+ strncpy(buffer_conf_print, "REQUIRED", SIZEOF(buffer_conf_print));
+ break;
+ }
+ break;
+
+ case CONFTYPE_TAPERALGO:
+ strncpy(buffer_conf_print, taperalgo2str(val->v.i), SIZEOF(buffer_conf_print));
+ break;
+
+ case CONFTYPE_PRIORITY:
+ switch(val->v.i) {
+ case 0:
+ strncpy(buffer_conf_print, "LOW", SIZEOF(buffer_conf_print));
+ break;
+
+ case 1:
+ strncpy(buffer_conf_print, "MEDIUM", SIZEOF(buffer_conf_print));
+ break;
+
+ case 2:
+ strncpy(buffer_conf_print, "HIGH", SIZEOF(buffer_conf_print));
+ break;
+ }
+ break;
+ }
+ buffer_conf_print[SIZEOF(buffer_conf_print) - 1] = '\0';
+ return buffer_conf_print;
+}
+
+void
+conf_init_string(
+ val_t *val,
+ char *s)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_STRING;
+ if(s)
+ val->v.s = stralloc(s);
+ else
+ val->v.s = NULL;
+}
+
+void
+conf_init_ident(
+ val_t *val,
+ char *s)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_IDENT;
+ if(s)
+ val->v.s = stralloc(s);
+ else
+ val->v.s = NULL;
+}
+
+void
+conf_init_int(
+ val_t *val,
+ int i)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_INT;
+ val->v.i = i;
+}
+
+void
+conf_init_bool(
+ val_t *val,
+ int i)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_BOOL;
+ val->v.i = i;
+}
+
+void
+conf_init_strategy(
+ val_t *val,
+ int i)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_STRATEGY;
+ val->v.i = i;
+}
+
+void
+conf_init_estimate(
+ val_t *val,
+ int i)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_ESTIMATE;
+ val->v.i = i;
+}
+
+void
+conf_init_taperalgo(
+ val_t *val,
+ int i)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_TAPERALGO;
+ val->v.i = i;
+}
+
+void
+conf_init_priority(
+ val_t *val,
+ int i)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_PRIORITY;
+ val->v.i = i;
+}
+
+void
+conf_init_compress(
+ val_t *val,
+ comp_t i)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_COMPRESS;
+ val->v.i = (int)i;
+}
+
+void
+conf_init_encrypt(
+ val_t *val,
+ encrypt_t i)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_ENCRYPT;
+ val->v.i = (int)i;
+}
+
+void
+conf_init_holding(
+ val_t *val,
+ dump_holdingdisk_t i)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_HOLDING;
+ val->v.i = (int)i;
+}
+
+void
+conf_init_long(
+ val_t *val,
+ long l)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_LONG;
+ val->v.l = l;
+}
+
+void
+conf_init_size(
+ val_t *val,
+ ssize_t sz)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_SIZE;
+ val->v.size = sz;
+}
+
+void
+conf_init_am64(
+ val_t *val,
+ off_t l)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_AM64;
+ val->v.am64 = l;
+}
+
+void
+conf_init_real(
+ val_t *val,
+ double r)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_REAL;
+ val->v.r = r;
+}
+
+void
+conf_init_rate(
+ val_t *val,
+ double r1,
+ double r2)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_RATE;
+ val->v.rate[0] = r1;
+ val->v.rate[1] = r2;
+}
+
+void
+conf_init_time(
+ val_t *val,
+ time_t t)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_TIME;
+ val->v.t = t;
+}
+
+void
+conf_init_sl(
+ val_t *val,
+ sl_t *sl)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_AM64;
+ val->v.sl = sl;
+}
+
+void
+conf_init_exinclude(
+ val_t *val)
+{
+ val->seen = 0;
+ val->type = CONFTYPE_EXINCLUDE;
+ val->v.exinclude.type = 0;
+ val->v.exinclude.optional = 0;
+ val->v.exinclude.sl = NULL;
+}
+
+void
+conf_set_string(
+ val_t *val,
+ char *s)
+{
+ val->seen = -1;
+ val->type = CONFTYPE_STRING;
+ amfree(val->v.s);
+ val->v.s = stralloc(s);
+}
+
+void
+conf_set_int(
+ val_t *val,
+ int i)
+{
+ val->seen = -1;
+ val->type = CONFTYPE_INT;
+ val->v.i = i;
+}
+
+void
+conf_set_bool(
+ val_t *val,
+ int i)
+{
+ val->seen = -1;
+ val->type = CONFTYPE_BOOL;
+ val->v.i = i;
+}
+
+void
+conf_set_compress(
+ val_t *val,
+ comp_t i)
+{
+ val->seen = -1;
+ val->type = CONFTYPE_COMPRESS;
+ val->v.i = (int)i;
+}
+
+void
+conf_set_encrypt(
+ val_t *val,
+ encrypt_t i)
+{
+ val->seen = -1;
+ val->type = CONFTYPE_COMPRESS;
+ val->v.i = (int)i;
+}
+
+void
+conf_set_holding(
+ val_t *val,
+ dump_holdingdisk_t i)
+{
+ val->seen = -1;
+ val->type = CONFTYPE_HOLDING;
+ val->v.i = (int)i;
+}
+
+void
+conf_set_strategy(
+ val_t *val,
+ int i)
+{
+ val->seen = -1;
+ val->type = CONFTYPE_STRATEGY;
+ val->v.i = i;
+}
+
+
+int
+get_conftype_int(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_INT) {
+ error("get_conftype_int: val.type is not CONFTYPE_INT");
+ /*NOTREACHED*/
+ }
+ return val->v.i;
+}
+
+long
+get_conftype_long(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_LONG) {
+ error("get_conftype_long: val.type is not CONFTYPE_LONG");
+ /*NOTREACHED*/
+ }
+ return val->v.l;
+}
+
+off_t
+get_conftype_am64(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_AM64) {
+ error("get_conftype_am64: val.type is not CONFTYPE_AM64");
+ /*NOTREACHED*/
+ }
+ return val->v.am64;
+}
+
+double
+get_conftype_real(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_REAL) {
+ error("get_conftype_real: val.type is not CONFTYPE_REAL");
+ /*NOTREACHED*/
+ }
+ return val->v.r;
+}
+
+char *
+get_conftype_string(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_STRING) {
+ error("get_conftype_string: val.type is not CONFTYPE_STRING");
+ /*NOTREACHED*/
+ }
+ return val->v.s;
+}
+
+char *
+get_conftype_ident(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_IDENT) {
+ error("get_conftype_ident: val.type is not CONFTYPE_IDENT");
+ /*NOTREACHED*/
+ }
+ return val->v.s;
+}
+
+time_t
+get_conftype_time(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_TIME) {
+ error("get_conftype_time: val.type is not CONFTYPE_TIME");
+ /*NOTREACHED*/
+ }
+ return val->v.t;
+}
+
+ssize_t
+get_conftype_size(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_SIZE) {
+ error("get_conftype_size: val.type is not CONFTYPE_SIZE");
+ /*NOTREACHED*/
+ }
+ return val->v.size;
+}
+
+sl_t *
+get_conftype_sl(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_SL) {
+ error("get_conftype_size: val.type is not CONFTYPE_SL");
+ /*NOTREACHED*/
+ }
+ return val->v.sl;
+}
+
+int
+get_conftype_bool(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_BOOL) {
+ error("get_conftype_bool: val.type is not CONFTYPE_BOOL");
+ /*NOTREACHED*/
+ }
+ return val->v.i;
+}
+
+int
+get_conftype_hold(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_HOLDING) {
+ error("get_conftype_hold: val.type is not CONFTYPE_HOLDING");
+ /*NOTREACHED*/
+ }
+ return val->v.i;
+}
+
+int
+get_conftype_compress(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_COMPRESS) {
+ error("get_conftype_compress: val.type is not CONFTYPE_COMPRESS");
+ /*NOTREACHED*/
+ }
+ return val->v.i;
+}
+
+int
+get_conftype_encrypt(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_ENCRYPT) {
+ error("get_conftype_encrypt: val.type is not CONFTYPE_ENCRYPT");
+ /*NOTREACHED*/
+ }
+ return val->v.i;
+}
+
+int
+get_conftype_estimate(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_ESTIMATE) {
+ error("get_conftype_extimate: val.type is not CONFTYPE_ESTIMATE");
+ /*NOTREACHED*/
+ }
+ return val->v.i;
+}
+
+int
+get_conftype_strategy(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_STRATEGY) {
+ error("get_conftype_strategy: val.type is not CONFTYPE_STRATEGY");
+ /*NOTREACHED*/
+ }
+ return val->v.i;
+}
+
+int
+get_conftype_taperalgo(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_TAPERALGO) {
+ error("get_conftype_taperalgo: val.type is not CONFTYPE_TAPERALGO");
+ /*NOTREACHED*/
+ }
+ return val->v.i;
+}
+
+int
+get_conftype_priority(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_PRIORITY) {
+ error("get_conftype_priority: val.type is not CONFTYPE_PRIORITY");
+ /*NOTREACHED*/
+ }
+ return val->v.i;
+}
+
+exinclude_t
+get_conftype_exinclude(
+ val_t *val)
+{
+ if (val->type != CONFTYPE_EXINCLUDE) {
+ error("get_conftype_exinclude: val.type is not CONFTYPE_EXINCLUDE");
+ /*NOTREACHED*/
+ }
+ return val->v.exinclude;
+}
+
+
+void
+dump_sockaddr(
+ struct sockaddr_in * sa)
+{
+ dbprintf(("%s: (sockaddr_in *)%p = { %d, %hd, %s }\n",
+ debug_prefix(NULL), sa, sa->sin_family, sa->sin_port,
+ inet_ntoa(sa->sin_addr)));
+}
+
+void
+read_block(
+ command_option_t *command_options,
+ t_conf_var *read_var,
+ keytab_t *keytab,
+ val_t *valarray,
+ char *prefix,
+ char *errormsg,
+ int read_brace,
+ void (*copy_function)(void))
+{
+ t_conf_var *np;
+ int saved_conf_line_num;
+ int done;
+
+ if(read_brace) {
+ get_conftoken(CONF_LBRACE);
+ get_conftoken(CONF_NL);
+ }
+
+ done = 0;
+ do {
+ conf_line_num += 1;
+ get_conftoken(CONF_ANY);
+ switch(tok) {
+ case CONF_RBRACE:
+ done = 1;
+ break;
+ case CONF_NL: /* empty line */
+ break;
+ case CONF_END: /* end of file */
+ done = 1;
+ break;
+ case CONF_IDENT:
+ case CONF_STRING:
+ if(copy_function)
+ copy_function();
+ else
+ conf_parserror("ident not expected");
+ break;
+ default:
+ {
+ for(np = read_var; np->token != CONF_UNKNOWN; np++)
+ if(np->token == tok) break;
+
+ if(np->token == CONF_UNKNOWN)
+ conf_parserror(errormsg);
+ else {
+ np->read_function(np, &valarray[np->parm]);
+ if(np->validate)
+ np->validate(np, &valarray[np->parm]);
+ }
+ }
+ }
+ if(tok != CONF_NL && tok != CONF_END && tok != CONF_RBRACE)
+ get_conftoken(CONF_NL);
+ } while(!done);
+
+ /* overwrite with command line option */
+ saved_conf_line_num = conf_line_num;
+ command_overwrite(command_options, read_var, keytab, valarray, prefix);
+ conf_line_num = saved_conf_line_num;
+}
+
+void
+command_overwrite(
+ command_option_t *command_options,
+ t_conf_var *overwrite_var,
+ keytab_t *keytab,
+ val_t *valarray,
+ char *prefix)
+{
+ t_conf_var *np;
+ keytab_t *kt;
+ char *myprefix;
+ command_option_t *command_option;
+
+ if(!command_options) return;
+
+ for(np = overwrite_var; np->token != CONF_UNKNOWN; np++) {
+ for(kt = keytab; kt->token != CONF_UNKNOWN; kt++)
+ if(kt->token == np->token) break;
+
+ if(kt->token == CONF_UNKNOWN) {
+ error("read_conf: invalid token");
+ /* NOTREACHED */
+ }
+
+ for(command_option = command_options; command_option->name != NULL;
+ command_option++) {
+ myprefix = stralloc2(prefix, kt->keyword);
+ if(strcasecmp(myprefix, command_option->name) == 0) {
+ command_option->used = 1;
+ valarray[np->parm].seen = -2;
+ if(np->type == CONFTYPE_STRING &&
+ command_option->value[0] != '"') {
+ conf_line = vstralloc("\"", command_option->value, "\"",
+ NULL);
+ }
+ else {
+ conf_line = stralloc(command_option->value);
+ }
+ conf_char = conf_line;
+ token_pushed = 0;
+ conf_line_num = -2;
+ np->read_function(np, &valarray[np->parm]);
+ amfree(conf_line);
+ conf_line = conf_char = NULL;
+
+ if(np->validate)
+ np->validate(np, &valarray[np->parm]);
+ }
+ amfree(myprefix);
+ }
+ }
+}
+
+void
+free_new_argv(
+ int new_argc,
+ char **new_argv)
+{
+ int i;
+ for(i=0; i<new_argc; i++)
+ amfree(new_argv[i]);
+ amfree(new_argv);
+}
+
+
+#ifndef HAVE_LIBREADLINE
+/*
+ * simple readline() replacements
+ */
+
+char *
+readline(
+ const char *prompt)
+{
+ printf("%s", prompt);
+ fflush(stdout);
+ fflush(stderr);
+ return agets(stdin);
+}
+
+void
+add_history(
+ const char *line)
+{
+ (void)line; /* Quite unused parameter warning */
+}
+#endif