X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=rcfile_y.y;h=b32a6b0cfa9143024eb3287edb280afc2a561986;hb=e75c62b234a699557c023970466a8accd0b5a7b2;hp=ea95dfad6976fb51ee8c0244593f31de8a7e5e20;hpb=cb236290e267f17d5f81a18d742b81f1b74b05a2;p=~andy%2Ffetchmail diff --git a/rcfile_y.y b/rcfile_y.y index ea95dfad..b32a6b0c 100644 --- a/rcfile_y.y +++ b/rcfile_y.y @@ -5,11 +5,13 @@ * For license terms, see the file COPYING in this directory. */ -#include +#include "config.h" #include #include #include +#if defined(HAVE_SYS_WAIT_H) #include +#endif #include #include #if defined(STDC_HEADERS) @@ -20,38 +22,68 @@ #endif #include +#if defined(__CYGWIN__) +#include +#endif /* __CYGWIN__ */ + #include "fetchmail.h" +#include "i18n.h" + +/* parser reads these */ +char *rcfile; /* path name of rc file */ +struct query cmd_opts; /* where to put command-line info */ -struct query cmd_opts; /* where to put command-line info */ +/* parser sets these */ struct query *querylist; /* head of server list (globally visible) */ -int yydebug; /* in case we didn't generate with -- debug */ +int yydebug; /* in case we didn't generate with -- debug */ -static struct query current; /* current server record */ +static struct query current; /* current server record */ static int prc_errflag; +static struct hostdata *leadentry; +static flag trailer; + +static void record_current(void); +static void user_reset(void); +static void reset_server(const char *name, int skip); + +/* these should be of size PATH_MAX */ +char currentwd[1024] = "", rcfiledir[1024] = ""; -static void prc_register(); -static void prc_reset(); +/* using Bison, this arranges that yydebug messages will show actual tokens */ +extern char * yytext; +#define YYPRINT(fp, type, val) fprintf(fp, " = \"%s\"", yytext) %} %union { int proto; - int flag; int number; char *sval; } -%token DEFAULTS POLL PROTOCOL AUTHENTICATE TIMEOUT KPOP KERBEROS -%token USERNAME PASSWORD FOLDER SMTPHOST MDA IS HERE THERE TO MAP LIMIT -%token SET BATCHLIMIT -%token PROTO +%token DEFAULTS POLL SKIP VIA AKA LOCALDOMAINS PROTOCOL +%token AUTHENTICATE TIMEOUT KPOP SDPS ENVELOPE QVIRTUAL +%token USERNAME PASSWORD FOLDER SMTPHOST FETCHDOMAINS MDA BSMTP LMTP +%token SMTPADDRESS SMTPNAME SPAMRESPONSE PRECONNECT POSTCONNECT LIMIT WARNINGS +%token INTERFACE MONITOR PLUGIN PLUGOUT +%token IS HERE THERE TO MAP +%token BATCHLIMIT FETCHLIMIT FETCHSIZELIMIT FASTUIDL EXPUNGE PROPERTIES +%token SET LOGFILE DAEMON SYSLOG IDFILE PIDFILE INVISIBLE POSTMASTER BOUNCEMAIL +%token SPAMBOUNCE SOFTBOUNCE SHOWDOTS +%token BADHEADER ACCEPT REJECT_ +%token PROTO AUTHTYPE %token STRING %token NUMBER -%token KEEP FLUSH FETCHALL REWRITE PORT SKIP +%token NO KEEP FLUSH LIMITFLUSH FETCHALL REWRITE FORCECR STRIPCR PASS8BITS +%token DROPSTATUS DROPDELIVERED +%token DNS SERVICE PORT UIDL INTERVAL MIMEDECODE IDLE CHECKALIAS +%token SSL SSLKEY SSLCERT SSLPROTO SSLCERTCK SSLCERTFILE SSLCERTPATH SSLCOMMONNAME SSLFINGERPRINT +%token PRINCIPAL ESMTPNAME ESMTPPASSWORD +%token TRACEPOLLS -/* these are actually used by the lexer */ -%token FLAG_TRUE 2 -%token FLAG_FALSE 1 +%expect 2 + +%destructor { free ($$); } STRING %% @@ -63,50 +95,163 @@ statement_list : statement | statement_list statement ; -/* future global options should also have the form SET */ -statement : define_server serverspecs userspecs - | SET BATCHLIMIT MAP NUMBER {batchlimit = $4;} +optmap : MAP | /* EMPTY */; + +/* future global options should also have the form SET optmap */ +statement : SET LOGFILE optmap STRING {run.logfile = prependdir ($4, rcfiledir); free($4);} + | SET IDFILE optmap STRING {run.idfile = prependdir ($4, rcfiledir); free($4);} + | SET PIDFILE optmap STRING {run.pidfile = prependdir ($4, rcfiledir); free($4);} + | SET DAEMON optmap NUMBER {run.poll_interval = $4;} + | SET POSTMASTER optmap STRING {run.postmaster = $4;} + | SET BOUNCEMAIL {run.bouncemail = TRUE;} + | SET NO BOUNCEMAIL {run.bouncemail = FALSE;} + | SET SPAMBOUNCE {run.spambounce = TRUE;} + | SET NO SPAMBOUNCE {run.spambounce = FALSE;} + | SET SOFTBOUNCE {run.softbounce = TRUE;} + | SET NO SOFTBOUNCE {run.softbounce = FALSE;} + | SET PROPERTIES optmap STRING {run.properties = $4;} + | SET SYSLOG {run.use_syslog = TRUE;} + | SET NO SYSLOG {run.use_syslog = FALSE;} + | SET INVISIBLE {run.invisible = TRUE;} + | SET NO INVISIBLE {run.invisible = FALSE;} + | SET SHOWDOTS {run.showdots = FLAG_TRUE;} + | SET NO SHOWDOTS {run.showdots = FLAG_FALSE;} + +/* + * The way the next two productions are written depends on the fact that + * userspecs cannot be empty. It's a kluge to deal with files that set + * up a load of defaults and then have poll statements following with no + * user options at all. + */ + | define_server serverspecs {record_current();} + | define_server serverspecs userspecs + +/* detect and complain about the most common user error */ + | define_server serverspecs userspecs serv_option + {yyerror(GT_("server option after user options"));} ; -define_server : POLL STRING {strcpy(current.servername, $2);} - | SKIP STRING {strcpy(current.servername, $2); - current.skip=($1==FLAG_TRUE);} - | DEFAULTS {strcpy(current.servername,"defaults");} +define_server : POLL STRING {reset_server($2, FALSE); free($2);} + | SKIP STRING {reset_server($2, TRUE); free($2);} + | DEFAULTS {reset_server("defaults", FALSE);} ; serverspecs : /* EMPTY */ | serverspecs serv_option ; -serv_option : PROTOCOL PROTO {current.protocol = $2;} +alias_list : STRING {save_str(¤t.server.akalist,$1,0); free($1);} + | alias_list STRING {save_str(¤t.server.akalist,$2,0); free($2);} + ; + +domain_list : STRING {save_str(¤t.server.localdomains,$1,0); free($1);} + | domain_list STRING {save_str(¤t.server.localdomains,$2,0); free($2);} + ; + +serv_option : AKA alias_list + | VIA STRING {current.server.via = $2;} + | LOCALDOMAINS domain_list + | PROTOCOL PROTO {current.server.protocol = $2;} | PROTOCOL KPOP { - current.protocol = P_POP3; - current.authenticate = A_KERBEROS; - current.port = KPOP_PORT; + current.server.protocol = P_POP3; + + if (current.server.authenticate == A_PASSWORD) +#ifdef KERBEROS_V5 + current.server.authenticate = A_KERBEROS_V5; +#else + current.server.authenticate = A_KERBEROS_V4; +#endif /* KERBEROS_V5 */ + current.server.service = KPOP_PORT; + } + | PRINCIPAL STRING {current.server.principal = $2;} + | ESMTPNAME STRING {current.server.esmtp_name = $2;} + | ESMTPPASSWORD STRING {current.server.esmtp_password = $2;} + | PROTOCOL SDPS { +#ifdef SDPS_ENABLE + current.server.protocol = P_POP3; + current.server.sdps = TRUE; +#else + yyerror(GT_("SDPS not enabled.")); +#endif /* SDPS_ENABLE */ } - | PORT NUMBER {current.port = $2;} - | AUTHENTICATE PASSWORD {current.authenticate = A_PASSWORD;} - | AUTHENTICATE KERBEROS {current.authenticate = A_KERBEROS;} - | TIMEOUT NUMBER {current.timeout = $2;} + | UIDL {current.server.uidl = FLAG_TRUE;} + | NO UIDL {current.server.uidl = FLAG_FALSE;} + | CHECKALIAS {current.server.checkalias = FLAG_TRUE;} + | NO CHECKALIAS {current.server.checkalias = FLAG_FALSE;} + | SERVICE STRING { + current.server.service = $2; + } + | SERVICE NUMBER { + int port = $2; + char buf[10]; + snprintf(buf, sizeof buf, "%d", port); + current.server.service = xstrdup(buf); + } + | PORT NUMBER { + int port = $2; + char buf[10]; + snprintf(buf, sizeof buf, "%d", port); + current.server.service = xstrdup(buf); + } + | INTERVAL NUMBER + {current.server.interval = $2;} + | AUTHENTICATE AUTHTYPE + {current.server.authenticate = $2;} + | TIMEOUT NUMBER + {current.server.timeout = $2;} + | ENVELOPE NUMBER STRING + { + current.server.envelope = $3; + current.server.envskip = $2; + } + | ENVELOPE STRING + { + current.server.envelope = $2; + current.server.envskip = 0; + } + + | QVIRTUAL STRING {current.server.qvirtual = $2;} + | INTERFACE STRING { +#ifdef CAN_MONITOR + interface_parse($2, ¤t.server); +#else + fprintf(stderr, GT_("fetchmail: interface option is only supported under Linux (without IPv6) and FreeBSD\n")); +#endif + free($2); + } + | MONITOR STRING { +#ifdef CAN_MONITOR + current.server.monitor = $2; +#else + fprintf(stderr, GT_("fetchmail: monitor option is only supported under Linux (without IPv6) and FreeBSD\n")); + free($2); +#endif + } + | PLUGIN STRING { current.server.plugin = $2; } + | PLUGOUT STRING { current.server.plugout = $2; } + | DNS {current.server.dns = FLAG_TRUE;} + | NO DNS {current.server.dns = FLAG_FALSE;} + | NO ENVELOPE {current.server.envelope = STRING_DISABLED;} + | TRACEPOLLS {current.server.tracepolls = FLAG_TRUE;} + | NO TRACEPOLLS {current.server.tracepolls = FLAG_FALSE;} + | BADHEADER ACCEPT {current.server.badheader = BHACCEPT;} + | BADHEADER REJECT_ {current.server.badheader = BHREJECT;} ; -/* the first and only the first user spec may omit the USERNAME part */ -userspecs : /* EMPTY */ - | user1opts {prc_register(); prc_reset();} - | user1opts explicits {prc_register(); prc_reset();} +userspecs : user1opts {record_current(); user_reset();} | explicits ; -explicits : explicitdef {prc_register(); prc_reset();} - | explicits explicitdef {prc_register(); prc_reset();} +explicits : explicitdef {record_current(); user_reset();} + | explicits explicitdef {record_current(); user_reset();} ; explicitdef : userdef user0opts ; -userdef : USERNAME STRING {strcpy(current.remotename, $2);} +userdef : USERNAME STRING {current.remotename = $2;} | USERNAME mapping_list HERE - | USERNAME STRING THERE {strcpy(current.remotename, $2);} + | USERNAME STRING THERE {current.remotename = $2;} ; user0opts : /* EMPTY */ @@ -121,27 +266,113 @@ mapping_list : mapping | mapping_list mapping ; -mapping : STRING - {save_id_pair(¤t.localnames, $1, NULL);} - | STRING MAP STRING - {save_id_pair(¤t.localnames, $1, $3);} +mapping : STRING {if (0 == strcmp($1, "*")) { + current.wildcard = TRUE; + } else { + save_str_pair(¤t.localnames, $1, NULL); + } + free($1);} + | STRING MAP STRING {save_str_pair(¤t.localnames, $1, $3); free($1); free($3);} + ; + +folder_list : STRING {save_str(¤t.mailboxes,$1,0); free($1);} + | folder_list STRING {save_str(¤t.mailboxes,$2,0); free($2);} + ; + +smtp_list : STRING {save_str(¤t.smtphunt, $1,TRUE); free($1);} + | smtp_list STRING {save_str(¤t.smtphunt, $2,TRUE); free($2);} + ; + +fetch_list : STRING {save_str(¤t.domainlist, $1,TRUE); free($1);} + | fetch_list STRING {save_str(¤t.domainlist, $2,TRUE); free($2);} + ; + +num_list : NUMBER + { + struct idlist *id; + id = save_str(¤t.antispam,STRING_DUMMY,0); + id->val.status.num = $1; + } + | num_list NUMBER + { + struct idlist *id; + id = save_str(¤t.antispam,STRING_DUMMY,0); + id->val.status.num = $2; + } ; user_option : TO mapping_list HERE | TO mapping_list | IS mapping_list HERE | IS mapping_list - | IS STRING THERE {strcpy(current.remotename, $2);} - | PASSWORD STRING {strcpy(current.password, $2);} - | FOLDER STRING {strcpy(current.mailbox, $2);} - | SMTPHOST STRING {strcpy(current.smtphost, $2);} - | MDA STRING {strcpy(current.mda, $2);} - - | KEEP {current.keep = ($1==FLAG_TRUE);} - | FLUSH {current.flush = ($1==FLAG_TRUE);} - | FETCHALL {current.fetchall = ($1==FLAG_TRUE);} - | REWRITE {current.norewrite = ($1==FLAG_TRUE);} - | LIMIT NUMBER {current.limit = $2;} + + | IS STRING THERE {current.remotename = $2;} + | PASSWORD STRING {current.password = $2;} + | FOLDER folder_list + | SMTPHOST smtp_list + | FETCHDOMAINS fetch_list + | SMTPADDRESS STRING {current.smtpaddress = $2;} + | SMTPNAME STRING {current.smtpname = $2;} + | SPAMRESPONSE num_list + | MDA STRING {current.mda = $2;} + | BSMTP STRING {current.bsmtp = prependdir ($2, rcfiledir); free($2);} + | LMTP {current.listener = LMTP_MODE;} + | PRECONNECT STRING {current.preconnect = $2;} + | POSTCONNECT STRING {current.postconnect = $2;} + + | KEEP {current.keep = FLAG_TRUE;} + | FLUSH {current.flush = FLAG_TRUE;} + | LIMITFLUSH {current.limitflush = FLAG_TRUE;} + | FETCHALL {current.fetchall = FLAG_TRUE;} + | REWRITE {current.rewrite = FLAG_TRUE;} + | FORCECR {current.forcecr = FLAG_TRUE;} + | STRIPCR {current.stripcr = FLAG_TRUE;} + | PASS8BITS {current.pass8bits = FLAG_TRUE;} + | DROPSTATUS {current.dropstatus = FLAG_TRUE;} + | DROPDELIVERED {current.dropdelivered = FLAG_TRUE;} + | MIMEDECODE {current.mimedecode = FLAG_TRUE;} + | IDLE {current.idle = FLAG_TRUE;} + + | SSL { +#ifdef SSL_ENABLE + current.use_ssl = FLAG_TRUE; +#else + yyerror(GT_("SSL is not enabled")); +#endif + } + | SSLKEY STRING {current.sslkey = prependdir ($2, rcfiledir); free($2);} + | SSLCERT STRING {current.sslcert = prependdir ($2, rcfiledir); free($2);} + | SSLPROTO STRING {current.sslproto = $2;} + | SSLCERTCK {current.sslcertck = FLAG_TRUE;} + | SSLCERTFILE STRING {current.sslcertfile = prependdir($2, rcfiledir); free($2);} + | SSLCERTPATH STRING {current.sslcertpath = prependdir($2, rcfiledir); free($2);} + | SSLCOMMONNAME STRING {current.sslcommonname = $2;} + | SSLFINGERPRINT STRING {current.sslfingerprint = $2;} + + | NO KEEP {current.keep = FLAG_FALSE;} + | NO FLUSH {current.flush = FLAG_FALSE;} + | NO LIMITFLUSH {current.limitflush = FLAG_FALSE;} + | NO FETCHALL {current.fetchall = FLAG_FALSE;} + | NO REWRITE {current.rewrite = FLAG_FALSE;} + | NO FORCECR {current.forcecr = FLAG_FALSE;} + | NO STRIPCR {current.stripcr = FLAG_FALSE;} + | NO PASS8BITS {current.pass8bits = FLAG_FALSE;} + | NO DROPSTATUS {current.dropstatus = FLAG_FALSE;} + | NO DROPDELIVERED {current.dropdelivered = FLAG_FALSE;} + | NO MIMEDECODE {current.mimedecode = FLAG_FALSE;} + | NO IDLE {current.idle = FLAG_FALSE;} + + | NO SSL {current.use_ssl = FLAG_FALSE;} + + | LIMIT NUMBER {current.limit = NUM_VALUE_IN($2);} + | WARNINGS NUMBER {current.warnings = NUM_VALUE_IN($2);} + | FETCHLIMIT NUMBER {current.fetchlimit = NUM_VALUE_IN($2);} + | FETCHSIZELIMIT NUMBER {current.fetchsizelimit = NUM_VALUE_IN($2);} + | FASTUIDL NUMBER {current.fastuidl = NUM_VALUE_IN($2);} + | BATCHLIMIT NUMBER {current.batchlimit = NUM_VALUE_IN($2);} + | EXPUNGE NUMBER {current.expunge = NUM_VALUE_IN($2);} + + | PROPERTIES STRING {current.properties = $2;} ; %% @@ -156,85 +387,133 @@ static struct query *hosttail; /* where to add new elements */ void yyerror (const char *s) /* report a syntax error */ { - fprintf(stderr,"%s line %d: %s at %s\n", rcfile, prc_lineno, s, yytext); + report_at_line(stderr, 0, rcfile, prc_lineno, GT_("%s at %s"), s, + (yytext && yytext[0]) ? yytext : GT_("end of input")); prc_errflag++; } -int prc_filecheck(pathname) -/* check that a configuration file is secure */ -const char *pathname; /* pathname for the configuration file */ +/** check that a configuration file is secure, returns PS_* status codes */ +int prc_filecheck(const char *pathname, + const flag securecheck /** shortcuts permission, filetype and uid tests if false */) { +#ifndef __EMX__ struct stat statbuf; + errno = 0; + + /* special case useful for debugging purposes */ + if (strcmp("/dev/null", pathname) == 0) + return(PS_SUCCESS); + + /* pass through the special name for stdin */ + if (strcmp("-", pathname) == 0) + return(PS_SUCCESS); + /* the run control file must have the same uid as the REAL uid of this process, it must have permissions no greater than 600, and it must not be a symbolic link. We check these conditions here. */ - errno = 0; - if (lstat(pathname, &statbuf) < 0) { + if (stat(pathname, &statbuf) < 0) { if (errno == ENOENT) - return(0); + return(PS_SUCCESS); else { - perror(pathname); + report(stderr, "lstat: %s: %s\n", pathname, strerror(errno)); return(PS_IOERR); } } - if ((statbuf.st_mode & S_IFLNK) == S_IFLNK) { - fprintf(stderr, "File %s must not be a symbolic link.\n", pathname); - return(PS_AUTHFAIL); + if (!securecheck) return PS_SUCCESS; + + if (!S_ISREG(statbuf.st_mode)) + { + fprintf(stderr, GT_("File %s must be a regular file.\n"), pathname); + return(PS_IOERR); } - if (statbuf.st_mode & ~(S_IFREG | S_IREAD | S_IWRITE)) { - fprintf(stderr, "File %s must have no more than -rw------ permissions.\n", +#ifndef __BEOS__ +#ifdef __CYGWIN__ + if (cygwin_internal(CW_CHECK_NTSEC, pathname)) +#endif /* __CYGWIN__ */ + if (statbuf.st_mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH)) + { + fprintf(stderr, GT_("File %s must have no more than -rwx------ (0700) permissions.\n"), pathname); - return(PS_AUTHFAIL); + return(PS_IOERR); } - - if (statbuf.st_uid != getuid()) { - fprintf(stderr, "File %s must be owned by you.\n", pathname); - return(PS_AUTHFAIL); +#endif /* __BEOS__ */ + +#ifdef HAVE_GETEUID + if (statbuf.st_uid != geteuid()) +#else + if (statbuf.st_uid != getuid()) +#endif /* HAVE_GETEUID */ + { + fprintf(stderr, GT_("File %s must be owned by you.\n"), pathname); + return(PS_IOERR); } - - return(0); +#endif + return(PS_SUCCESS); } -int prc_parse_file (pathname) +int prc_parse_file (const char *pathname, const flag securecheck) /* digest the configuration into a linked list of host records */ -const char *pathname; /* pathname for the configuration file */ { prc_errflag = 0; querylist = hosttail = (struct query *)NULL; - prc_reset(); + + errno = 0; /* Check that the file is secure */ - if ((prc_errflag = prc_filecheck(pathname)) != 0) + if ( (prc_errflag = prc_filecheck(pathname, securecheck)) != 0 ) return(prc_errflag); - if (errno == ENOENT) - return(0); - - /* Open the configuration and feed it to the lexer. */ - if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) { - perror(pathname); + /* + * Croak if the configuration directory does not exist. + * This probably means an NFS mount failed and we can't + * see a configuration file that ought to be there. + * Question: is this a portable check? It's not clear + * that all implementations of lstat() will return ENOTDIR + * rather than plain ENOENT in this case... + */ + if (errno == ENOTDIR) + return(PS_IOERR); + else if (errno == ENOENT) + return(PS_SUCCESS); + + /* Open the configuration file and feed it to the lexer. */ + if (strcmp(pathname, "-") == 0) + yyin = stdin; + else if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) { + report(stderr, "open: %s: %s\n", pathname, strerror(errno)); return(PS_IOERR); } yyparse(); /* parse entire file */ - fclose(yyin); + fclose(yyin); /* not checking this should be safe, file mode was r */ if (prc_errflag) return(PS_SYNTAX); else - return(0); + return(PS_SUCCESS); +} + +static void reset_server(const char *name, int skip) +/* clear the entire global record and initialize it with a new name */ +{ + trailer = FALSE; + memset(¤t,'\0',sizeof(current)); + current.smtp_socket = -1; + current.server.pollname = xstrdup(name); + current.server.skip = skip; + current.server.principal = (char *)NULL; } -static void prc_reset(void) -/* clear the global current record (server parameters) used by the parser */ + +static void user_reset(void) +/* clear the global current record (user parameters) used by the parser */ { - char savename[HOSTLEN+1]; - int saveport, saveproto, saveauth, saveskip; + struct hostdata save; /* * Purpose of this code is to initialize the new server block, but @@ -242,23 +521,17 @@ static void prc_reset(void) * preserve server options unless the command-line explicitly * overrides them. */ - (void) strcpy(savename, current.servername); - saveport = current.port; - saveproto = current.protocol; - saveauth = current.authenticate; - saveskip = current.skip; + save = current.server; memset(¤t, '\0', sizeof(current)); + current.smtp_socket = -1; - (void) strcpy(current.servername, savename); - current.protocol = saveproto; - current.authenticate = saveauth; - current.skip = saveskip; + current.server = save; } -struct query *hostalloc(init) -/* append a host record to the host list */ -struct query *init; /* pointer to block containing initial values */ +/** append a host record to the host list */ +struct query *hostalloc(struct query *init /** pointer to block containing + initial values */) { struct query *node; @@ -266,7 +539,13 @@ struct query *init; /* pointer to block containing initial values */ node = (struct query *) xmalloc(sizeof(struct query)); /* initialize it */ - memcpy(node, init, sizeof(struct query)); + if (init) + memcpy(node, init, sizeof(struct query)); + else + { + memset(node, '\0', sizeof(struct query)); + node->smtp_socket = -1; + } /* append to end of list */ if (hosttail != (struct query *) 0) @@ -274,66 +553,40 @@ struct query *init; /* pointer to block containing initial values */ else querylist = node; /* list is empty */ hosttail = node; + + if (trailer) + node->server.lead_server = leadentry; + else + { + node->server.lead_server = NULL; + leadentry = &node->server; + } + return(node); } -static void prc_register(void) +static void record_current(void) /* register current parameters and append to the host list */ { -#define STR_FORCE(fld, len) if (cmd_opts.fld[0]) \ - strcpy(current.fld, cmd_opts.fld) - STR_FORCE(remotename, USERNAMELEN); - STR_FORCE(password, PASSWORDLEN); - STR_FORCE(mailbox, FOLDERLEN); - STR_FORCE(smtphost, HOSTLEN); - STR_FORCE(mda, MDALEN); -#undef STR_FORCE - -#define FLAG_FORCE(fld) if (cmd_opts.fld) current.fld = cmd_opts.fld - FLAG_FORCE(protocol); - FLAG_FORCE(keep); - FLAG_FORCE(flush); - FLAG_FORCE(fetchall); - FLAG_FORCE(norewrite); - FLAG_FORCE(skip); - FLAG_FORCE(port); - FLAG_FORCE(authenticate); - FLAG_FORCE(timeout); - FLAG_FORCE(limit); -#undef FLAG_FORCE - (void) hostalloc(¤t); + trailer = TRUE; } -void optmerge(struct query *h2, struct query *h1) -/* merge two options records; empty fields in h2 are filled in from h1 */ +char *prependdir (const char *file, const char *dir) +/* if a filename is relative to dir, convert it to an absolute path */ { - append_uid_list(&h2->localnames, &h1->localnames); - -#define STR_MERGE(fld, len) if (*(h2->fld) == '\0') strcpy(h2->fld, h1->fld) - STR_MERGE(remotename, USERNAMELEN); - STR_MERGE(password, PASSWORDLEN); - STR_MERGE(mailbox, FOLDERLEN); - STR_MERGE(smtphost, HOSTLEN); - STR_MERGE(mda, MDALEN); -#undef STR_MERGE - -#define FLAG_MERGE(fld) if (!h2->fld) h2->fld = h1->fld - FLAG_MERGE(protocol); - FLAG_MERGE(keep); - FLAG_MERGE(flush); - FLAG_MERGE(fetchall); - FLAG_MERGE(norewrite); - FLAG_MERGE(skip); - FLAG_MERGE(port); - FLAG_MERGE(authenticate); - FLAG_MERGE(timeout); - FLAG_MERGE(limit); -#undef FLAG_MERGE - + char *newfile; + if (!file[0] || /* null path */ + file[0] == '/' || /* absolute path */ + strcmp(file, "-") == 0 || /* stdin/stdout */ + !dir[0]) /* we don't HAVE_GETCWD */ + return xstrdup (file); + newfile = (char *)xmalloc (strlen (dir) + 1 + strlen (file) + 1); + if (dir[strlen(dir) - 1] != '/') + sprintf (newfile, "%s/%s", dir, file); + else + sprintf (newfile, "%s%s", dir, file); + return newfile; } -/* easier to do this than cope with variations in where the library lives */ -int yywrap(void) {return 1;} - /* rcfile_y.y ends here */