1 %{
2 /*
3  * rcfile_y.y -- Run control file parser for fetchmail
4  *
5  * For license terms, see the file COPYING in this directory.
6  */
7 
8 #include "config.h"
9 #include "fetchmail.h"
10 #include <stdio.h>
11 #include <sys/types.h>
12 #include <sys/file.h>
13 #if defined(HAVE_SYS_WAIT_H)
14 #include <sys/wait.h>
15 #endif
16 #include <sys/stat.h>
17 #include <errno.h>
18 #if defined(STDC_HEADERS)
19 #include <stdlib.h>
20 #endif
21 #if defined(HAVE_UNISTD_H)
22 #include <unistd.h>
23 #endif
24 #include <string.h>
25 
26 #if defined(__CYGWIN__)
27 #include <sys/cygwin.h>
28 #endif /* __CYGWIN__ */
29 
30 #include "fetchmail.h"
31 #include "i18n.h"
32 
33 /* parser reads these */
34 char *rcfile;			/* path name of rc file */
35 struct query cmd_opts;		/* where to put command-line info */
36 
37 /* parser sets these */
38 struct query *querylist;	/* head of server list (globally visible) */
39 
40 static struct query current;	/* current server record */
41 static int prc_errflag;
42 static struct hostdata *leadentry;
43 static flag trailer;
44 
45 static void record_current(void);
46 static void user_reset(void);
47 static void reset_server(const char *name, int skip);
48 
49 /* these should be of size PATH_MAX */
50 char currentwd[1024] = "", rcfiledir[1024] = "";
51 
52 /* using Bison, this arranges that yydebug messages will show actual tokens */
53 extern char * yytext;
54 #define YYPRINT(fp, type, val)	fprintf(fp, " = \"%s\"", yytext)
55 %}
56 
57 %union {
58   int proto;
59   int number;
60   char *sval;
61 }
62 
63 %token DEFAULTS POLL SKIP VIA AKA LOCALDOMAINS PROTOCOL
64 %token AUTHENTICATE TIMEOUT KPOP SDPS ENVELOPE QVIRTUAL
65 %token USERNAME PASSWORD FOLDER SMTPHOST FETCHDOMAINS MDA BSMTP LMTP
66 %token SMTPADDRESS SMTPNAME SPAMRESPONSE PRECONNECT POSTCONNECT LIMIT WARNINGS
67 %token INTERFACE MONITOR PLUGIN PLUGOUT
68 %token IS HERE THERE TO MAP
69 %token BATCHLIMIT FETCHLIMIT FETCHSIZELIMIT FASTUIDL EXPUNGE PROPERTIES
70 %token SET LOGFILE DAEMON SYSLOG IDFILE PIDFILE INVISIBLE POSTMASTER BOUNCEMAIL
71 %token SPAMBOUNCE SOFTBOUNCE SHOWDOTS
72 %token BADHEADER ACCEPT REJECT_
73 %token <proto> PROTO AUTHTYPE
74 %token <sval>  STRING
75 %token <number> NUMBER
76 %token NO KEEP FLUSH LIMITFLUSH FETCHALL REWRITE FORCECR STRIPCR PASS8BITS
77 %token DROPSTATUS DROPDELIVERED
78 %token DNS SERVICE PORT UIDL INTERVAL MIMEDECODE IDLE CHECKALIAS
79 %token SSL SSLKEY SSLCERT SSLPROTO SSLCERTCK SSLCERTFILE SSLCERTPATH SSLCOMMONNAME SSLFINGERPRINT
80 %token PRINCIPAL ESMTPNAME ESMTPPASSWORD
81 %token TRACEPOLLS
82 
83 %expect 2
84 
85 %destructor { free ($$); } STRING
86 
87 %%
88 
89 rcfile		: /* empty */
90 		| statement_list
91 		;
92 
93 statement_list	: statement
94 		| statement_list statement
95 		;
96 
97 optmap		: MAP | /* EMPTY */;
98 
99 /* future global options should also have the form SET <name> optmap <value> */
100 statement	: SET LOGFILE optmap STRING	{run.logfile = prependdir ($4, rcfiledir); free($4);}
101 		| SET IDFILE optmap STRING	{run.idfile = prependdir ($4, rcfiledir); free($4);}
102 		| SET PIDFILE optmap STRING	{run.pidfile = prependdir ($4, rcfiledir); free($4);}
103 		| SET DAEMON optmap NUMBER	{run.poll_interval = $4;}
104 		| SET POSTMASTER optmap STRING	{run.postmaster = $4;}
105 		| SET BOUNCEMAIL		{run.bouncemail = TRUE;}
106 		| SET NO BOUNCEMAIL		{run.bouncemail = FALSE;}
107 		| SET SPAMBOUNCE		{run.spambounce = TRUE;}
108 		| SET NO SPAMBOUNCE		{run.spambounce = FALSE;}
109 		| SET SOFTBOUNCE		{run.softbounce = TRUE;}
110 		| SET NO SOFTBOUNCE		{run.softbounce = FALSE;}
111 		| SET PROPERTIES optmap STRING	{run.properties = $4;}
112 		| SET SYSLOG			{run.use_syslog = TRUE;}
113 		| SET NO SYSLOG			{run.use_syslog = FALSE;}
114 		| SET INVISIBLE			{run.invisible = TRUE;}
115 		| SET NO INVISIBLE		{run.invisible = FALSE;}
116 		| SET SHOWDOTS			{run.showdots = FLAG_TRUE;}
117 		| SET NO SHOWDOTS		{run.showdots = FLAG_FALSE;}
118 
119 /*
120  * The way the next two productions are written depends on the fact that
121  * userspecs cannot be empty.  It's a kluge to deal with files that set
122  * up a load of defaults and then have poll statements following with no
123  * user options at all.
124  */
125 		| define_server serverspecs		{record_current();}
126 		| define_server serverspecs userspecs
127 
128 /* detect and complain about the most common user error */
129 		| define_server serverspecs userspecs serv_option
130 			{yyerror(GT_("server option after user options"));}
131 		;
132 
133 define_server	: POLL STRING		{reset_server($2, FALSE); free($2);}
134 		| SKIP STRING		{reset_server($2, TRUE);  free($2);}
135 		| DEFAULTS		{reset_server("defaults", FALSE);}
136   		;
137 
138 serverspecs	: /* EMPTY */
139 		| serverspecs serv_option
140 		;
141 
142 alias_list	: STRING		{save_str(&current.server.akalist,$1,0); free($1);}
143 		| alias_list STRING	{save_str(&current.server.akalist,$2,0); free($2);}
144 		;
145 
146 domain_list	: STRING		{save_str(&current.server.localdomains,$1,0); free($1);}
147 		| domain_list STRING	{save_str(&current.server.localdomains,$2,0); free($2);}
148 		;
149 
150 serv_option	: AKA alias_list
151 		| VIA STRING		{current.server.via = $2;}
152 		| LOCALDOMAINS domain_list
153 		| PROTOCOL PROTO	{current.server.protocol = $2;}
154 		| PROTOCOL KPOP		{
155 					    current.server.protocol = P_POP3;
156 
157 					    if (current.server.authenticate == A_PASSWORD)
158 #ifdef KERBEROS_V5
159 						current.server.authenticate = A_KERBEROS_V5;
160 #else
161 						current.server.authenticate = A_KERBEROS_V4;
162 #endif /* KERBEROS_V5 */
163 					    current.server.service = KPOP_PORT;
164 					}
165 		| PRINCIPAL STRING	{current.server.principal = $2;}
166 		| ESMTPNAME STRING	{current.server.esmtp_name = $2;}
167 		| ESMTPPASSWORD STRING	{current.server.esmtp_password = $2;}
168 		| PROTOCOL SDPS		{
169 #ifdef SDPS_ENABLE
170 					    current.server.protocol = P_POP3;
171 					    current.server.sdps = TRUE;
172 #else
173 					    yyerror(GT_("SDPS not enabled."));
174 #endif /* SDPS_ENABLE */
175 					}
176 		| UIDL			{current.server.uidl = FLAG_TRUE;}
177 		| NO UIDL		{current.server.uidl  = FLAG_FALSE;}
178 		| CHECKALIAS            {current.server.checkalias = FLAG_TRUE;}
179 		| NO CHECKALIAS         {current.server.checkalias  = FLAG_FALSE;}
180 		| SERVICE STRING	{
181 					current.server.service = $2;
182 					}
183 		| SERVICE NUMBER	{
184 					int port = $2;
185 					char buf[10];
186 					snprintf(buf, sizeof buf, "%d", port);
187 					current.server.service = xstrdup(buf);
188 		}
189 		| PORT NUMBER		{
190 					int port = $2;
191 					char buf[10];
192 					snprintf(buf, sizeof buf, "%d", port);
193 					current.server.service = xstrdup(buf);
194 		}
195 		| INTERVAL NUMBER
196 			{current.server.interval = $2;}
197 		| AUTHENTICATE AUTHTYPE
198 			{current.server.authenticate = $2;}
199 		| TIMEOUT NUMBER
200 			{current.server.timeout = $2;}
201 		| ENVELOPE NUMBER STRING
202 					{
203 					    current.server.envelope = $3;
204 					    current.server.envskip = $2;
205 					}
206 		| ENVELOPE STRING
207 					{
208 					    current.server.envelope = $2;
209 					    current.server.envskip = 0;
210 					}
211 
212 		| QVIRTUAL STRING	{current.server.qvirtual = $2;}
213 		| INTERFACE STRING	{
214 #ifdef CAN_MONITOR
215 					interface_parse($2, &current.server);
216 #else
217 					fprintf(stderr, GT_("fetchmail: interface option is only supported under Linux (without IPv6) and FreeBSD\n"));
218 #endif
219 					free($2);
220 					}
221 		| MONITOR STRING	{
222 #ifdef CAN_MONITOR
223 					current.server.monitor = $2;
224 #else
225 					fprintf(stderr, GT_("fetchmail: monitor option is only supported under Linux (without IPv6) and FreeBSD\n"));
226 					free($2);
227 #endif
228 					}
229 		| PLUGIN STRING		{ current.server.plugin = $2; }
230 		| PLUGOUT STRING	{ current.server.plugout = $2; }
231 		| DNS			{current.server.dns = FLAG_TRUE;}
232 		| NO DNS		{current.server.dns = FLAG_FALSE;}
233 		| NO ENVELOPE		{current.server.envelope = STRING_DISABLED;}
234 		| TRACEPOLLS		{current.server.tracepolls = FLAG_TRUE;}
235 		| NO TRACEPOLLS		{current.server.tracepolls = FLAG_FALSE;}
236 		| BADHEADER ACCEPT	{current.server.badheader = BHACCEPT;}
237 		| BADHEADER REJECT_	{current.server.badheader = BHREJECT;}
238 		;
239 
240 userspecs	: user1opts		{record_current(); user_reset();}
241 		| explicits
242 		;
243 
244 explicits	: explicitdef		{record_current(); user_reset();}
245 		| explicits explicitdef	{record_current(); user_reset();}
246 		;
247 
248 explicitdef	: userdef user0opts
249 		;
250 
251 userdef		: USERNAME STRING	{current.remotename = $2;}
252 		| USERNAME mapping_list HERE
253 		| USERNAME STRING THERE	{current.remotename = $2;}
254 		;
255 
256 user0opts	: /* EMPTY */
257 		| user0opts user_option
258 		;
259 
260 user1opts	: user_option
261 		| user1opts user_option
262 		;
263 
264 mapping_list	: mapping
265 		| mapping_list mapping
266 		;
267 
268 mapping		: STRING		{if (0 == strcmp($1, "*")) {
269 					      current.wildcard = TRUE;
270 					  } else {
271 					    save_str_pair(&current.localnames, $1, NULL);
272 					  }
273 					 free($1);}
274 		| STRING MAP STRING	{save_str_pair(&current.localnames, $1, $3); free($1); free($3);}
275 		;
276 
277 folder_list	: STRING		{save_str(&current.mailboxes,$1,0); free($1);}
278 		| folder_list STRING	{save_str(&current.mailboxes,$2,0); free($2);}
279 		;
280 
281 smtp_list	: STRING		{save_str(&current.smtphunt, $1,TRUE); free($1);}
282 		| smtp_list STRING	{save_str(&current.smtphunt, $2,TRUE); free($2);}
283 		;
284 
285 fetch_list	: STRING		{save_str(&current.domainlist, $1,TRUE); free($1);}
286 		| fetch_list STRING	{save_str(&current.domainlist, $2,TRUE); free($2);}
287 		;
288 
289 num_list	: NUMBER
290 			{
291 			    struct idlist *id;
292 			    id = save_str(&current.antispam,STRING_DUMMY,0);
293 			    id->val.status.num = $1;
294 			}
295 		| num_list NUMBER
296 			{
297 			    struct idlist *id;
298 			    id = save_str(&current.antispam,STRING_DUMMY,0);
299 			    id->val.status.num = $2;
300 			}
301 		;
302 
303 user_option	: TO mapping_list HERE
304 		| TO mapping_list
305 		| IS mapping_list HERE
306 		| IS mapping_list
307 
308 		| IS STRING THERE	{current.remotename  = $2;}
309 		| PASSWORD STRING	{current.password    = $2;}
310 		| FOLDER folder_list
311 		| SMTPHOST smtp_list
312 		| FETCHDOMAINS fetch_list
313 		| SMTPADDRESS STRING	{current.smtpaddress = $2;}
314 		| SMTPNAME STRING	{current.smtpname =    $2;}
315 		| SPAMRESPONSE num_list
316 		| MDA STRING		{current.mda         = $2;}
317 		| BSMTP STRING		{current.bsmtp       = prependdir ($2, rcfiledir); free($2);}
318 		| LMTP			{current.listener    = LMTP_MODE;}
319 		| PRECONNECT STRING	{current.preconnect  = $2;}
320 		| POSTCONNECT STRING	{current.postconnect = $2;}
321 
322 		| KEEP			{current.keep        = FLAG_TRUE;}
323 		| FLUSH			{current.flush       = FLAG_TRUE;}
324 		| LIMITFLUSH		{current.limitflush  = FLAG_TRUE;}
325 		| FETCHALL		{current.fetchall    = FLAG_TRUE;}
326 		| REWRITE		{current.rewrite     = FLAG_TRUE;}
327 		| FORCECR		{current.forcecr     = FLAG_TRUE;}
328 		| STRIPCR		{current.stripcr     = FLAG_TRUE;}
329 		| PASS8BITS		{current.pass8bits   = FLAG_TRUE;}
330 		| DROPSTATUS		{current.dropstatus  = FLAG_TRUE;}
331                 | DROPDELIVERED         {current.dropdelivered = FLAG_TRUE;}
332 		| MIMEDECODE		{current.mimedecode  = FLAG_TRUE;}
333 		| IDLE			{current.idle        = FLAG_TRUE;}
334 
335 		| SSL 	                {
336 #ifdef SSL_ENABLE
337 		    current.use_ssl = FLAG_TRUE;
338 #else
339 		    yyerror(GT_("SSL is not enabled"));
340 #endif
341 		}
342 		| SSLKEY STRING		{current.sslkey = prependdir ($2, rcfiledir); free($2);}
343 		| SSLCERT STRING	{current.sslcert = prependdir ($2, rcfiledir); free($2);}
344 		| SSLPROTO STRING	{current.sslproto = $2;}
345 		| SSLCERTCK             {current.sslcertck = FLAG_TRUE;}
346 		| SSLCERTFILE STRING    {current.sslcertfile = prependdir($2, rcfiledir); free($2);}
347 		| SSLCERTPATH STRING    {current.sslcertpath = prependdir($2, rcfiledir); free($2);}
348 		| SSLCOMMONNAME STRING  {current.sslcommonname = $2;}
349 		| SSLFINGERPRINT STRING {current.sslfingerprint = $2;}
350 
351 		| NO KEEP		{current.keep        = FLAG_FALSE;}
352 		| NO FLUSH		{current.flush       = FLAG_FALSE;}
353 		| NO LIMITFLUSH		{current.limitflush  = FLAG_FALSE;}
354 		| NO FETCHALL		{current.fetchall    = FLAG_FALSE;}
355 		| NO REWRITE		{current.rewrite     = FLAG_FALSE;}
356 		| NO FORCECR		{current.forcecr     = FLAG_FALSE;}
357 		| NO STRIPCR		{current.stripcr     = FLAG_FALSE;}
358 		| NO PASS8BITS		{current.pass8bits   = FLAG_FALSE;}
359 		| NO DROPSTATUS		{current.dropstatus  = FLAG_FALSE;}
360                 | NO DROPDELIVERED      {current.dropdelivered = FLAG_FALSE;}
361 		| NO MIMEDECODE		{current.mimedecode  = FLAG_FALSE;}
362 		| NO IDLE		{current.idle        = FLAG_FALSE;}
363 
364 		| NO SSL		{current.use_ssl     = FLAG_FALSE;}
365 		| NO SSLCERTCK		{current.sslcertck   = FLAG_FALSE;}
366 
367 		| LIMIT NUMBER		{current.limit       = NUM_VALUE_IN($2);}
368 		| WARNINGS NUMBER	{current.warnings    = NUM_VALUE_IN($2);}
369 		| FETCHLIMIT NUMBER	{current.fetchlimit  = NUM_VALUE_IN($2);}
370 		| FETCHSIZELIMIT NUMBER	{current.fetchsizelimit = NUM_VALUE_IN($2);}
371 		| FASTUIDL NUMBER	{current.fastuidl    = NUM_VALUE_IN($2);}
372 		| BATCHLIMIT NUMBER	{current.batchlimit  = NUM_VALUE_IN($2);}
373 		| EXPUNGE NUMBER	{current.expunge     = NUM_VALUE_IN($2);}
374 
375 		| PROPERTIES STRING	{current.properties  = $2;}
376 		;
377 %%
378 
379 /* lexer interface */
380 extern char *rcfile;
381 extern int prc_lineno;
382 extern char *yytext;
383 extern FILE *yyin;
384 
385 static struct query *hosttail;	/* where to add new elements */
386 
yyerror(const char * s)387 void yyerror (const char *s)
388 /* report a syntax error */
389 {
390     report_at_line(stderr, 0, rcfile, prc_lineno, GT_("%s at %s"), s,
391 		   (yytext && yytext[0]) ? yytext : GT_("end of input"));
392     prc_errflag++;
393 }
394 
395 /** check that a configuration file is secure, returns PS_* status codes */
prc_filecheck(const char * pathname,const flag securecheck)396 int prc_filecheck(const char *pathname,
397 		  const flag securecheck /** shortcuts permission, filetype and uid tests if false */)
398 {
399 #ifndef __EMX__
400     struct stat statbuf;
401 
402     errno = 0;
403 
404     /* special case useful for debugging purposes */
405     if (strcmp("/dev/null", pathname) == 0)
406 	return(PS_SUCCESS);
407 
408     /* pass through the special name for stdin */
409     if (strcmp("-", pathname) == 0)
410 	return(PS_SUCCESS);
411 
412     /* the run control file must have the same uid as the REAL uid of this
413        process, it must have permissions no greater than 600, and it must not
414        be a symbolic link.  We check these conditions here. */
415 
416     if (stat(pathname, &statbuf) < 0) {
417 	if (errno == ENOENT)
418 	    return(PS_SUCCESS);
419 	else {
420 	    report(stderr, "lstat: %s: %s\n", pathname, strerror(errno));
421 	    return(PS_IOERR);
422 	}
423     }
424 
425     if (!securecheck)	return PS_SUCCESS;
426 
427     if (!S_ISREG(statbuf.st_mode))
428     {
429 	fprintf(stderr, GT_("File %s must be a regular file.\n"), pathname);
430 	return(PS_IOERR);
431     }
432 
433 #ifndef __BEOS__
434 #ifdef __CYGWIN__
435     if (cygwin_internal(CW_CHECK_NTSEC, pathname))
436 #endif /* __CYGWIN__ */
437     if (statbuf.st_mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH))
438     {
439 	fprintf(stderr, GT_("File %s must have no more than -rwx------ (0700) permissions.\n"),
440 		pathname);
441 	return(PS_IOERR);
442     }
443 #endif /* __BEOS__ */
444 
445 #ifdef HAVE_GETEUID
446     if (statbuf.st_uid != geteuid())
447 #else
448     if (statbuf.st_uid != getuid())
449 #endif /* HAVE_GETEUID */
450     {
451 	fprintf(stderr, GT_("File %s must be owned by you.\n"), pathname);
452 	return(PS_IOERR);
453     }
454 #endif
455     return(PS_SUCCESS);
456 }
457 
prc_parse_file(const char * pathname,const flag securecheck)458 int prc_parse_file (const char *pathname, const flag securecheck)
459 /* digest the configuration into a linked list of host records */
460 {
461     prc_errflag = 0;
462     querylist = hosttail = (struct query *)NULL;
463 
464     errno = 0;
465 
466     /* Check that the file is secure */
467     if ( (prc_errflag = prc_filecheck(pathname, securecheck)) != 0 )
468 	return(prc_errflag);
469 
470     /*
471      * Croak if the configuration directory does not exist.
472      * This probably means an NFS mount failed and we can't
473      * see a configuration file that ought to be there.
474      * Question: is this a portable check? It's not clear
475      * that all implementations of lstat() will return ENOTDIR
476      * rather than plain ENOENT in this case...
477      */
478     if (errno == ENOTDIR)
479 	return(PS_IOERR);
480     else if (errno == ENOENT)
481 	return(PS_SUCCESS);
482 
483     /* Open the configuration file and feed it to the lexer. */
484     if (strcmp(pathname, "-") == 0)
485 	yyin = stdin;
486     else if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
487 	report(stderr, "open: %s: %s\n", pathname, strerror(errno));
488 	return(PS_IOERR);
489     }
490 
491     yyparse();		/* parse entire file */
492 
493     if (yyin != stdin)
494        fclose(yyin);	/* not checking this should be safe, file mode was r */
495 
496     if (prc_errflag)
497 	return(PS_SYNTAX);
498     else
499 	return(PS_SUCCESS);
500 }
501 
reset_server(const char * name,int skip)502 static void reset_server(const char *name, int skip)
503 /* clear the entire global record and initialize it with a new name */
504 {
505     trailer = FALSE;
506     memset(&current,'\0',sizeof(current));
507     current.smtp_socket = -1;
508     current.server.pollname = xstrdup(name);
509     current.server.skip = skip;
510     current.server.principal = (char *)NULL;
511 }
512 
513 
user_reset(void)514 static void user_reset(void)
515 /* clear the global current record (user parameters) used by the parser */
516 {
517     struct hostdata save;
518 
519     /*
520      * Purpose of this code is to initialize the new server block, but
521      * preserve whatever server name was previously set.  Also
522      * preserve server options unless the command-line explicitly
523      * overrides them.
524      */
525     save = current.server;
526 
527     memset(&current, '\0', sizeof(current));
528     current.smtp_socket = -1;
529 
530     current.server = save;
531 }
532 
533 /** append a host record to the host list */
hostalloc(struct query * init)534 struct query *hostalloc(struct query *init /** pointer to block containing
535 					       initial values */)
536 {
537     struct query *node;
538 
539     /* allocate new node */
540     node = (struct query *) xmalloc(sizeof(struct query));
541 
542     /* initialize it */
543     if (init)
544 	memcpy(node, init, sizeof(struct query));
545     else
546     {
547 	memset(node, '\0', sizeof(struct query));
548 	node->smtp_socket = -1;
549     }
550 
551     /* append to end of list */
552     if (hosttail != (struct query *) 0)
553 	hosttail->next = node;	/* list contains at least one element */
554     else
555 	querylist = node;	/* list is empty */
556     hosttail = node;
557 
558     if (trailer)
559 	node->server.lead_server = leadentry;
560     else
561     {
562 	node->server.lead_server = NULL;
563 	leadentry = &node->server;
564     }
565 
566     return(node);
567 }
568 
record_current(void)569 static void record_current(void)
570 /* register current parameters and append to the host list */
571 {
572     (void) hostalloc(&current);
573     trailer = TRUE;
574 }
575 
prependdir(const char * file,const char * dir)576 char *prependdir (const char *file, const char *dir)
577 /* if a filename is relative to dir, convert it to an absolute path */
578 {
579     char *newfile;
580     if (!file[0] ||			/* null path */
581 	file[0] == '/' ||		/* absolute path */
582 	strcmp(file, "-") == 0 ||	/* stdin/stdout */
583 	!dir[0])			/* we don't HAVE_GETCWD */
584 	return xstrdup (file);
585     newfile = (char *)xmalloc (strlen (dir) + 1 + strlen (file) + 1);
586     if (dir[strlen(dir) - 1] != '/')
587 	sprintf (newfile, "%s/%s", dir, file);
588     else
589 	sprintf (newfile, "%s%s", dir, file);
590     return newfile;
591 }
592 
593 /* rcfile_y.y ends here */
594