X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=rcfile_y.y;h=91de6146178f10fd7c6ea66cc6789fe3b8056cbc;hb=d31db10231e9ed89f64fdf6e0fb7cae182aa377e;hp=7f36b3ddb98e07bba7ff5667a58c9369dab2bbb7;hpb=b1b6ddf9079453caa64484f31887cb4273c045bc;p=~andy%2Ffetchmail diff --git a/rcfile_y.y b/rcfile_y.y index 7f36b3dd..91de6146 100644 --- a/rcfile_y.y +++ b/rcfile_y.y @@ -22,12 +22,13 @@ #endif #include -#if NET_SECURITY -#include -#endif /* NET_SECURITY */ +#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 */ @@ -46,6 +47,9 @@ 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] = ""; + /* using Bison, this arranges that yydebug messages will show actual tokens */ extern char * yytext; #define YYPRINT(fp, type, val) fprintf(fp, " = \"%s\"", yytext) @@ -58,18 +62,28 @@ extern char * yytext; } %token DEFAULTS POLL SKIP VIA AKA LOCALDOMAINS PROTOCOL -%token AUTHENTICATE TIMEOUT KPOP SDPS KERBEROS4 KERBEROS5 KERBEROS -%token ENVELOPE QVIRTUAL USERNAME PASSWORD FOLDER SMTPHOST MDA BSMTP LMTP -%token SMTPADDRESS SPAMRESPONSE PRECONNECT POSTCONNECT LIMIT -%token NETSEC INTERFACE MONITOR PLUGIN PLUGOUT -%token IS HERE THERE TO MAP WILDCARD -%token BATCHLIMIT FETCHLIMIT EXPUNGE PROPERTIES -%token SET LOGFILE DAEMON SYSLOG IDFILE INVISIBLE POSTMASTER WARNINGS -%token PROTO +%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 NO KEEP FLUSH FETCHALL REWRITE FORCECR STRIPCR PASS8BITS DROPSTATUS -%token DNS SERVICE PORT UIDL INTERVAL MIMEDECODE CHECKALIAS +%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 + +%expect 2 + +%destructor { free ($$); } STRING %% @@ -84,12 +98,24 @@ statement_list : statement optmap : MAP | /* EMPTY */; /* future global options should also have the form SET optmap */ -statement : SET LOGFILE optmap STRING {run.logfile = xstrdup($4);} - | SET IDFILE optmap STRING {run.idfile = xstrdup($4);} +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 = xstrdup($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 @@ -102,11 +128,11 @@ statement : SET LOGFILE optmap STRING {run.logfile = xstrdup($4);} /* detect and complain about the most common user error */ | define_server serverspecs userspecs serv_option - {yyerror("server option after user options");} + {yyerror(GT_("server option after user options"));} ; -define_server : POLL STRING {reset_server($2, FALSE);} - | SKIP STRING {reset_server($2, TRUE);} +define_server : POLL STRING {reset_server($2, FALSE); free($2);} + | SKIP STRING {reset_server($2, TRUE); free($2);} | DEFAULTS {reset_server("defaults", FALSE);} ; @@ -114,39 +140,38 @@ serverspecs : /* EMPTY */ | serverspecs serv_option ; -alias_list : STRING {save_str(¤t.server.akalist,$1,0);} - | alias_list STRING {save_str(¤t.server.akalist,$2,0);} +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);} - | domain_list STRING {save_str(¤t.server.localdomains,$2,0);} +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 = xstrdup($2);} + | VIA STRING {current.server.via = $2;} | LOCALDOMAINS domain_list | PROTOCOL PROTO {current.server.protocol = $2;} | PROTOCOL KPOP { current.server.protocol = P_POP3; - if (current.server.preauthenticate == A_PASSWORD) + if (current.server.authenticate == A_PASSWORD) #ifdef KERBEROS_V5 - current.server.preauthenticate = A_KERBEROS_V5; + current.server.authenticate = A_KERBEROS_V5; #else - current.server.preauthenticate = A_KERBEROS_V4; + current.server.authenticate = A_KERBEROS_V4; #endif /* KERBEROS_V5 */ -#if INET6 current.server.service = KPOP_PORT; -#else /* INET6 */ - current.server.port = KPOP_PORT; -#endif /* INET6 */ } + | 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("SDPS not enabled."); + yyerror(GT_("SDPS not enabled.")); #endif /* SDPS_ENABLE */ } | UIDL {current.server.uidl = FLAG_TRUE;} @@ -154,76 +179,63 @@ serv_option : AKA alias_list | CHECKALIAS {current.server.checkalias = FLAG_TRUE;} | NO CHECKALIAS {current.server.checkalias = FLAG_FALSE;} | SERVICE STRING { -#if INET6 current.server.service = $2; -#endif /* INET6 */ } + | SERVICE NUMBER { + int port = $2; + char buf[10]; + snprintf(buf, sizeof buf, "%d", port); + current.server.service = xstrdup(buf); + } | PORT NUMBER { -#if !INET6 - current.server.port = $2; -#endif /* !INET6 */ - } - | INTERVAL NUMBER {current.server.interval = $2;} - | AUTHENTICATE PASSWORD {current.server.preauthenticate = A_PASSWORD;} - | AUTHENTICATE KERBEROS4 {current.server.preauthenticate = A_KERBEROS_V4;} - | AUTHENTICATE KERBEROS5 {current.server.preauthenticate = A_KERBEROS_V5;} - | AUTHENTICATE KERBEROS { -#ifdef KERBEROS_V5 - current.server.preauthenticate = A_KERBEROS_V5; -#else - current.server.preauthenticate = A_KERBEROS_V4; -#endif /* KERBEROS_V5 */ + int port = $2; + char buf[10]; + snprintf(buf, sizeof buf, "%d", port); + current.server.service = xstrdup(buf); } - | TIMEOUT NUMBER {current.server.timeout = $2;} - - | ENVELOPE NUMBER STRING + | INTERVAL NUMBER + {current.server.interval = $2;} + | AUTHENTICATE AUTHTYPE + {current.server.authenticate = $2;} + | TIMEOUT NUMBER + {current.server.timeout = $2;} + | ENVELOPE NUMBER STRING { - current.server.envelope = - xstrdup($3); + current.server.envelope = $3; current.server.envskip = $2; } | ENVELOPE STRING { - current.server.envelope = - xstrdup($2); + current.server.envelope = $2; current.server.envskip = 0; } - | QVIRTUAL STRING {current.server.qvirtual=xstrdup($2);} - | NETSEC STRING { -#ifdef NET_SECURITY - void *request; - int requestlen; - - if (net_security_strtorequest($2, &request, &requestlen)) - yyerror("invalid security request"); - else { - current.server.netsec = xstrdup($2); - free(request); - } -#else - yyerror("network-security support disabled"); -#endif /* NET_SECURITY */ - } + | QVIRTUAL STRING {current.server.qvirtual = $2;} | INTERFACE STRING { -#if defined(linux) && !defined(INET6) +#ifdef CAN_MONITOR interface_parse($2, ¤t.server); -#else /* defined(linux) && !defined(INET6) */ - fprintf(stderr, "fetchmail: interface option is only supported under Linux\n"); -#endif /* defined(linux) && !defined(INET6) */ +#else + fprintf(stderr, GT_("fetchmail: interface option is only supported under Linux (without IPv6) and FreeBSD\n")); +#endif + free($2); } | MONITOR STRING { -#if defined(linux) && !defined(INET6) - current.server.monitor = xstrdup($2); -#else /* defined(linux) && !defined(INET6) */ - fprintf(stderr, "fetchmail: monitor option is only supported under Linux\n"); -#endif /* defined(linux) && !defined(INET6) */ +#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 = xstrdup($2); } - | PLUGOUT STRING { current.server.plugout = xstrdup($2); } + | 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;} ; userspecs : user1opts {record_current(); user_reset();} @@ -237,9 +249,9 @@ explicits : explicitdef {record_current(); user_reset();} explicitdef : userdef user0opts ; -userdef : USERNAME STRING {current.remotename = xstrdup($2);} +userdef : USERNAME STRING {current.remotename = $2;} | USERNAME mapping_list HERE - | USERNAME STRING THERE {current.remotename = xstrdup($2);} + | USERNAME STRING THERE {current.remotename = $2;} ; user0opts : /* EMPTY */ @@ -250,87 +262,117 @@ user1opts : user_option | user1opts user_option ; -localnames : WILDCARD {current.wildcard = TRUE;} - | mapping_list {current.wildcard = FALSE;} - | mapping_list WILDCARD {current.wildcard = TRUE;} - ; - mapping_list : mapping | mapping_list mapping ; -mapping : STRING - {save_str_pair(¤t.localnames, $1, NULL);} - | STRING MAP STRING - {save_str_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);} - | folder_list STRING {save_str(¤t.mailboxes,$2,0);} +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);} - | smtp_list STRING {save_str(¤t.smtphunt, $2,TRUE);} +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 = 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 = save_str(¤t.antispam,STRING_DUMMY,0); id->val.status.num = $2; } ; -user_option : TO localnames HERE - | TO localnames - | IS localnames HERE - | IS localnames +user_option : TO mapping_list HERE + | TO mapping_list + | IS mapping_list HERE + | IS mapping_list - | IS STRING THERE {current.remotename = xstrdup($2);} - | PASSWORD STRING {current.password = xstrdup($2);} + | IS STRING THERE {current.remotename = $2;} + | PASSWORD STRING {current.password = $2;} | FOLDER folder_list | SMTPHOST smtp_list - | SMTPADDRESS STRING {current.smtpaddress = xstrdup($2);} + | FETCHDOMAINS fetch_list + | SMTPADDRESS STRING {current.smtpaddress = $2;} + | SMTPNAME STRING {current.smtpname = $2;} | SPAMRESPONSE num_list - | MDA STRING {current.mda = xstrdup($2);} - | BSMTP STRING {current.bsmtp = xstrdup($2);} + | MDA STRING {current.mda = $2;} + | BSMTP STRING {current.bsmtp = prependdir ($2, rcfiledir); free($2);} | LMTP {current.listener = LMTP_MODE;} - | PRECONNECT STRING {current.preconnect = xstrdup($2);} - | POSTCONNECT STRING {current.postconnect = xstrdup($2);} + | 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;} - | LIMIT NUMBER {current.limit = NUM_VALUE($2);} - | WARNINGS NUMBER {current.warnings = NUM_VALUE($2);} - | FETCHLIMIT NUMBER {current.fetchlimit = NUM_VALUE($2);} - | BATCHLIMIT NUMBER {current.batchlimit = NUM_VALUE($2);} - | EXPUNGE NUMBER {current.expunge = NUM_VALUE($2);} + | NO SSL {current.use_ssl = FLAG_FALSE;} - | PROPERTIES STRING {current.properties = xstrdup($2);} + | 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;} ; %% @@ -345,13 +387,14 @@ static struct query *hosttail; /* where to add new elements */ void yyerror (const char *s) /* report a syntax error */ { - error_at_line( 0, 0, rcfile, prc_lineno, "%s at %s", s, - (yytext && yytext[0]) ? yytext : "end of input"); + 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(const char *pathname, const flag securecheck) -/* check that a configuration file is secure */ +/** 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; @@ -370,34 +413,43 @@ int prc_filecheck(const char *pathname, const flag securecheck) process, it must have permissions no greater than 600, and it must not be a symbolic link. We check these conditions here. */ - if (lstat(pathname, &statbuf) < 0) { + if (stat(pathname, &statbuf) < 0) { if (errno == ENOENT) return(PS_SUCCESS); else { - error(0, errno, "lstat: %s", pathname); + report(stderr, "lstat: %s: %s\n", pathname, strerror(errno)); return(PS_IOERR); } } - if (!securecheck) return 0; + if (!securecheck) return PS_SUCCESS; - if ((statbuf.st_mode & S_IFLNK) == S_IFLNK) + if (!S_ISREG(statbuf.st_mode)) { - fprintf(stderr, "File %s must not be a symbolic link.\n", pathname); - return(PS_AUTHFAIL); + 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 | S_IEXEC | S_IXGRP)) +#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, "File %s must have no more than -rwx--x--- (0710) permissions.\n", + fprintf(stderr, GT_("File %s must have no more than -rwx------ (0700) permissions.\n"), pathname); - return(PS_AUTHFAIL); + return(PS_IOERR); } +#endif /* __BEOS__ */ +#ifdef HAVE_GETEUID + if (statbuf.st_uid != geteuid()) +#else if (statbuf.st_uid != getuid()) +#endif /* HAVE_GETEUID */ { - fprintf(stderr, "File %s must be owned by you.\n", pathname); - return(PS_AUTHFAIL); + fprintf(stderr, GT_("File %s must be owned by you.\n"), pathname); + return(PS_IOERR); } #endif return(PS_SUCCESS); @@ -415,20 +467,31 @@ int prc_parse_file (const char *pathname, const flag securecheck) if ( (prc_errflag = prc_filecheck(pathname, securecheck)) != 0 ) return(prc_errflag); - if (errno == ENOENT) + /* + * 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) { - error(0, errno, "open: %s", pathname); + report(stderr, "open: %s: %s\n", pathname, strerror(errno)); return(PS_IOERR); } yyparse(); /* parse entire file */ - fclose(yyin); + if (yyin != stdin) + fclose(yyin); /* not checking this should be safe, file mode was r */ if (prc_errflag) return(PS_SYNTAX); @@ -444,6 +507,7 @@ static void reset_server(const char *name, int skip) current.smtp_socket = -1; current.server.pollname = xstrdup(name); current.server.skip = skip; + current.server.principal = (char *)NULL; } @@ -466,9 +530,9 @@ static void user_reset(void) 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; @@ -509,9 +573,21 @@ static void record_current(void) trailer = TRUE; } -/* easier to do this than cope with variations in where the library lives */ -int yywrap(void) {return 1;} +char *prependdir (const char *file, const char *dir) +/* if a filename is relative to dir, convert it to an absolute path */ +{ + 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; +} /* rcfile_y.y ends here */ - -