]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/src/collect.c
unfinished sblive driver, playback/mixer only for now - not enabled in
[FreeBSD/FreeBSD.git] / contrib / sendmail / src / collect.c
1 /*
2  * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4  * Copyright (c) 1988, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * By using this file, you agree to the terms and conditions set
8  * forth in the LICENSE file which can be found at the top level of
9  * the sendmail distribution.
10  *
11  */
12
13 #ifndef lint
14 static char sccsid[] = "@(#)collect.c   8.93 (Berkeley) 1/26/1999";
15 #endif /* not lint */
16
17 # include <errno.h>
18 # include "sendmail.h"
19
20 /*
21 **  COLLECT -- read & parse message header & make temp file.
22 **
23 **      Creates a temporary file name and copies the standard
24 **      input to that file.  Leading UNIX-style "From" lines are
25 **      stripped off (after important information is extracted).
26 **
27 **      Parameters:
28 **              fp -- file to read.
29 **              smtpmode -- if set, we are running SMTP: give an RFC821
30 **                      style message to say we are ready to collect
31 **                      input, and never ignore a single dot to mean
32 **                      end of message.
33 **              hdrp -- the location to stash the header.
34 **              e -- the current envelope.
35 **
36 **      Returns:
37 **              none.
38 **
39 **      Side Effects:
40 **              Temp file is created and filled.
41 **              The from person may be set.
42 */
43
44 static jmp_buf  CtxCollectTimeout;
45 static void     collecttimeout __P((time_t));
46 static bool     CollectProgress;
47 static EVENT    *CollectTimeout;
48
49 /* values for input state machine */
50 #define IS_NORM         0       /* middle of line */
51 #define IS_BOL          1       /* beginning of line */
52 #define IS_DOT          2       /* read a dot at beginning of line */
53 #define IS_DOTCR        3       /* read ".\r" at beginning of line */
54 #define IS_CR           4       /* read a carriage return */
55
56 /* values for message state machine */
57 #define MS_UFROM        0       /* reading Unix from line */
58 #define MS_HEADER       1       /* reading message header */
59 #define MS_BODY         2       /* reading message body */
60 #define MS_DISCARD      3       /* discarding rest of message */
61
62 void
63 collect(fp, smtpmode, hdrp, e)
64         FILE *fp;
65         bool smtpmode;
66         HDR **hdrp;
67         register ENVELOPE *e;
68 {
69         register FILE *volatile tf;
70         volatile bool ignrdot = smtpmode ? FALSE : IgnrDot;
71         volatile time_t dbto = smtpmode ? TimeOuts.to_datablock : 0;
72         register char *volatile bp;
73         volatile int c = EOF;
74         volatile bool inputerr = FALSE;
75         bool headeronly;
76         char *volatile buf;
77         volatile int buflen;
78         volatile int istate;
79         volatile int mstate;
80         u_char *volatile pbp;
81         int hdrslen = 0;
82         u_char peekbuf[8];
83         char dfname[MAXQFNAME];
84         char bufbuf[MAXLINE];
85         extern bool isheader __P((char *));
86         extern void tferror __P((FILE *volatile, ENVELOPE *));
87
88         headeronly = hdrp != NULL;
89
90         /*
91         **  Create the temp file name and create the file.
92         */
93
94         if (!headeronly)
95         {
96                 int tfd;
97                 struct stat stbuf;
98
99                 strcpy(dfname, queuename(e, 'd'));
100                 tfd = dfopen(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode, SFF_ANYFILE);
101                 if (tfd < 0 || (tf = fdopen(tfd, "w")) == NULL)
102                 {
103                         syserr("Cannot create %s", dfname);
104                         e->e_flags |= EF_NO_BODY_RETN;
105                         finis(TRUE, ExitStat);
106                 }
107                 if (fstat(fileno(tf), &stbuf) < 0)
108                         e->e_dfino = -1;
109                 else
110                 {
111                         e->e_dfdev = stbuf.st_dev;
112                         e->e_dfino = stbuf.st_ino;
113                 }
114                 HasEightBits = FALSE;
115                 e->e_msgsize = 0;
116                 e->e_flags |= EF_HAS_DF;
117         }
118
119         /*
120         **  Tell ARPANET to go ahead.
121         */
122
123         if (smtpmode)
124                 message("354 Enter mail, end with \".\" on a line by itself");
125
126         if (tTd(30, 2))
127                 printf("collect\n");
128
129         /*
130         **  Read the message.
131         **
132         **      This is done using two interleaved state machines.
133         **      The input state machine is looking for things like
134         **      hidden dots; the message state machine is handling
135         **      the larger picture (e.g., header versus body).
136         */
137
138         buf = bp = bufbuf;
139         buflen = sizeof bufbuf;
140         pbp = peekbuf;
141         istate = IS_BOL;
142         mstate = SaveFrom ? MS_HEADER : MS_UFROM;
143         CollectProgress = FALSE;
144
145         if (dbto != 0)
146         {
147                 /* handle possible input timeout */
148                 if (setjmp(CtxCollectTimeout) != 0)
149                 {
150                         if (LogLevel > 2)
151                                 sm_syslog(LOG_NOTICE, e->e_id,
152                                     "timeout waiting for input from %s during message collect",
153                                     CurHostName ? CurHostName : "<local machine>");
154                         errno = 0;
155                         usrerr("451 timeout waiting for input during message collect");
156                         goto readerr;
157                 }
158                 CollectTimeout = setevent(dbto, collecttimeout, dbto);
159         }
160
161         for (;;)
162         {
163                 if (tTd(30, 35))
164                         printf("top, istate=%d, mstate=%d\n", istate, mstate);
165                 for (;;)
166                 {
167                         if (pbp > peekbuf)
168                                 c = *--pbp;
169                         else
170                         {
171                                 while (!feof(fp) && !ferror(fp))
172                                 {
173                                         errno = 0;
174                                         c = getc(fp);
175                                         if (errno != EINTR)
176                                                 break;
177                                         clearerr(fp);
178                                 }
179                                 CollectProgress = TRUE;
180                                 if (TrafficLogFile != NULL && !headeronly)
181                                 {
182                                         if (istate == IS_BOL)
183                                                 fprintf(TrafficLogFile, "%05d <<< ",
184                                                         (int) getpid());
185                                         if (c == EOF)
186                                                 fprintf(TrafficLogFile, "[EOF]\n");
187                                         else
188                                                 putc(c, TrafficLogFile);
189                                 }
190                                 if (c == EOF)
191                                         goto readerr;
192                                 if (SevenBitInput)
193                                         c &= 0x7f;
194                                 else
195                                         HasEightBits |= bitset(0x80, c);
196                         }
197                         if (tTd(30, 94))
198                                 printf("istate=%d, c=%c (0x%x)\n",
199                                         istate, c, c);
200                         switch (istate)
201                         {
202                           case IS_BOL:
203                                 if (c == '.')
204                                 {
205                                         istate = IS_DOT;
206                                         continue;
207                                 }
208                                 break;
209
210                           case IS_DOT:
211                                 if (c == '\n' && !ignrdot &&
212                                     !bitset(EF_NL_NOT_EOL, e->e_flags))
213                                         goto readerr;
214                                 else if (c == '\r' &&
215                                          !bitset(EF_CRLF_NOT_EOL, e->e_flags))
216                                 {
217                                         istate = IS_DOTCR;
218                                         continue;
219                                 }
220                                 else if (c != '.' ||
221                                          (OpMode != MD_SMTP &&
222                                           OpMode != MD_DAEMON &&
223                                           OpMode != MD_ARPAFTP))
224                                 {
225                                         *pbp++ = c;
226                                         c = '.';
227                                 }
228                                 break;
229
230                           case IS_DOTCR:
231                                 if (c == '\n' && !ignrdot)
232                                         goto readerr;
233                                 else
234                                 {
235                                         /* push back the ".\rx" */
236                                         *pbp++ = c;
237                                         *pbp++ = '\r';
238                                         c = '.';
239                                 }
240                                 break;
241
242                           case IS_CR:
243                                 if (c == '\n')
244                                         istate = IS_BOL;
245                                 else
246                                 {
247                                         ungetc(c, fp);
248                                         c = '\r';
249                                         istate = IS_NORM;
250                                 }
251                                 goto bufferchar;
252                         }
253
254                         if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags))
255                         {
256                                 istate = IS_CR;
257                                 continue;
258                         }
259                         else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags))
260                                 istate = IS_BOL;
261                         else
262                                 istate = IS_NORM;
263
264 bufferchar:
265                         if (!headeronly)
266                                 e->e_msgsize++;
267                         switch (mstate)
268                         {
269                           case MS_BODY:
270                                 /* just put the character out */
271                                 if (MaxMessageSize <= 0 ||
272                                     e->e_msgsize <= MaxMessageSize)
273                                         putc(c, tf);
274
275                                 /* fall through */
276
277                           case MS_DISCARD:
278                                 continue;
279                         }
280
281                         /* header -- buffer up */
282                         if (bp >= &buf[buflen - 2])
283                         {
284                                 char *obuf;
285
286                                 if (mstate != MS_HEADER)
287                                         break;
288
289                                 /* out of space for header */
290                                 obuf = buf;
291                                 if (buflen < MEMCHUNKSIZE)
292                                         buflen *= 2;
293                                 else
294                                         buflen += MEMCHUNKSIZE;
295                                 buf = xalloc(buflen);
296                                 bcopy(obuf, buf, bp - obuf);
297                                 bp = &buf[bp - obuf];
298                                 if (obuf != bufbuf)
299                                         free(obuf);
300                         }
301                         if (c >= 0200 && c <= 0237)
302                         {
303 #if 0   /* causes complaints -- figure out something for 8.9 */
304                                 usrerr("Illegal character 0x%x in header", c);
305 #endif
306                         }
307                         else if (c != '\0')
308                         {
309                                 *bp++ = c;
310                                 if (MaxHeadersLength > 0 &&
311                                     ++hdrslen > MaxHeadersLength)
312                                 {
313                                         sm_syslog(LOG_NOTICE, e->e_id,
314                                                   "headers too large (%d max) from %s during message collect",
315                                                   MaxHeadersLength,
316                                                   CurHostName != NULL ? CurHostName : "localhost");
317                                         errno = 0;
318                                         e->e_flags |= EF_CLRQUEUE;
319                                         e->e_status = "5.6.0";
320                                         usrerr("552 Headers too large (%d max)",
321                                                 MaxHeadersLength);
322                                         mstate = MS_DISCARD;
323                                 }
324                         }
325                         if (istate == IS_BOL)
326                                 break;
327                 }
328                 *bp = '\0';
329
330 nextstate:
331                 if (tTd(30, 35))
332                         printf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n",
333                                 istate, mstate, buf);
334                 switch (mstate)
335                 {
336                   case MS_UFROM:
337                         mstate = MS_HEADER;
338 #ifndef NOTUNIX
339                         if (strncmp(buf, "From ", 5) == 0)
340                         {
341                                 extern void eatfrom __P((char *volatile, ENVELOPE *));
342
343                                 bp = buf;
344                                 eatfrom(buf, e);
345                                 continue;
346                         }
347 #endif
348                         /* fall through */
349
350                   case MS_HEADER:
351                         if (!isheader(buf))
352                         {
353                                 mstate = MS_BODY;
354                                 goto nextstate;
355                         }
356
357                         /* check for possible continuation line */
358                         do
359                         {
360                                 clearerr(fp);
361                                 errno = 0;
362                                 c = getc(fp);
363                         } while (errno == EINTR);
364                         if (c != EOF)
365                                 ungetc(c, fp);
366                         if (c == ' ' || c == '\t')
367                         {
368                                 /* yep -- defer this */
369                                 continue;
370                         }
371
372                         /* trim off trailing CRLF or NL */
373                         if (*--bp != '\n' || *--bp != '\r')
374                                 bp++;
375                         *bp = '\0';
376
377                         if (bitset(H_EOH, chompheader(buf, FALSE, hdrp, e)))
378                         {
379                                 mstate = MS_BODY;
380                                 goto nextstate;
381                         }
382                         break;
383
384                   case MS_BODY:
385                         if (tTd(30, 1))
386                                 printf("EOH\n");
387                         if (headeronly)
388                                 goto readerr;
389                         bp = buf;
390
391                         /* toss blank line */
392                         if ((!bitset(EF_CRLF_NOT_EOL, e->e_flags) &&
393                                 bp[0] == '\r' && bp[1] == '\n') ||
394                             (!bitset(EF_NL_NOT_EOL, e->e_flags) &&
395                                 bp[0] == '\n'))
396                         {
397                                 break;
398                         }
399
400                         /* if not a blank separator, write it out */
401                         if (MaxMessageSize <= 0 ||
402                             e->e_msgsize <= MaxMessageSize)
403                         {
404                                 while (*bp != '\0')
405                                         putc(*bp++, tf);
406                         }
407                         break;
408                 }
409                 bp = buf;
410         }
411
412 readerr:
413         if ((feof(fp) && smtpmode) || ferror(fp))
414         {
415                 const char *errmsg = errstring(errno);
416
417                 if (tTd(30, 1))
418                         printf("collect: premature EOM: %s\n", errmsg);
419                 if (LogLevel >= 2)
420                         sm_syslog(LOG_WARNING, e->e_id,
421                                 "collect: premature EOM: %s", errmsg);
422                 inputerr = TRUE;
423         }
424
425         /* reset global timer */
426         clrevent(CollectTimeout);
427
428         if (headeronly)
429                 return;
430
431         if (tf != NULL &&
432             (fflush(tf) != 0 || ferror(tf) ||
433              (SuperSafe && fsync(fileno(tf)) < 0) ||
434              fclose(tf) < 0))
435         {
436                 tferror(tf, e);
437                 flush_errors(TRUE);
438                 finis(TRUE, ExitStat);
439         }
440
441         /* An EOF when running SMTP is an error */
442         if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
443         {
444                 char *host;
445                 char *problem;
446
447                 host = RealHostName;
448                 if (host == NULL)
449                         host = "localhost";
450
451                 if (feof(fp))
452                         problem = "unexpected close";
453                 else if (ferror(fp))
454                         problem = "I/O error";
455                 else
456                         problem = "read timeout";
457                 if (LogLevel > 0 && feof(fp))
458                         sm_syslog(LOG_NOTICE, e->e_id,
459                             "collect: %s on connection from %.100s, sender=%s: %s",
460                             problem, host,
461                             shortenstring(e->e_from.q_paddr, MAXSHORTSTR),
462                             errstring(errno));
463                 if (feof(fp))
464                         usrerr("451 collect: %s on connection from %s, from=%s",
465                                 problem, host,
466                                 shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
467                 else
468                         syserr("451 collect: %s on connection from %s, from=%s",
469                                 problem, host,
470                                 shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
471
472                 /* don't return an error indication */
473                 e->e_to = NULL;
474                 e->e_flags &= ~EF_FATALERRS;
475                 e->e_flags |= EF_CLRQUEUE;
476
477                 /* and don't try to deliver the partial message either */
478                 if (InChild)
479                         ExitStat = EX_QUIT;
480                 finis(TRUE, ExitStat);
481         }
482
483         /*
484         **  Find out some information from the headers.
485         **      Examples are who is the from person & the date.
486         */
487
488         eatheader(e, TRUE);
489
490         if (GrabTo && e->e_sendqueue == NULL)
491                 usrerr("No recipient addresses found in header");
492
493         /* collect statistics */
494         if (OpMode != MD_VERIFY)
495                 markstats(e, (ADDRESS *) NULL, FALSE);
496
497 #if _FFR_DSN_RRT_OPTION
498         /*
499         **  If we have a Return-Receipt-To:, turn it into a DSN.
500         */
501
502         if (RrtImpliesDsn && hvalue("return-receipt-to", e->e_header) != NULL)
503         {
504                 ADDRESS *q;
505
506                 for (q = e->e_sendqueue; q != NULL; q = q->q_next)
507                         if (!bitset(QHASNOTIFY, q->q_flags))
508                                 q->q_flags |= QHASNOTIFY|QPINGONSUCCESS;
509         }
510 #endif
511
512         /*
513         **  Add an Apparently-To: line if we have no recipient lines.
514         */
515
516         if (hvalue("to", e->e_header) != NULL ||
517             hvalue("cc", e->e_header) != NULL ||
518             hvalue("apparently-to", e->e_header) != NULL)
519         {
520                 /* have a valid recipient header -- delete Bcc: headers */
521                 e->e_flags |= EF_DELETE_BCC;
522         }
523         else if (hvalue("bcc", e->e_header) == NULL)
524         {
525                 /* no valid recipient headers */
526                 register ADDRESS *q;
527                 char *hdr = NULL;
528
529                 /* create an Apparently-To: field */
530                 /*    that or reject the message.... */
531                 switch (NoRecipientAction)
532                 {
533                   case NRA_ADD_APPARENTLY_TO:
534                         hdr = "Apparently-To";
535                         break;
536
537                   case NRA_ADD_TO:
538                         hdr = "To";
539                         break;
540
541                   case NRA_ADD_BCC:
542                         addheader("Bcc", " ", &e->e_header);
543                         break;
544
545                   case NRA_ADD_TO_UNDISCLOSED:
546                         addheader("To", "undisclosed-recipients:;", &e->e_header);
547                         break;
548                 }
549
550                 if (hdr != NULL)
551                 {
552                         for (q = e->e_sendqueue; q != NULL; q = q->q_next)
553                         {
554                                 if (q->q_alias != NULL)
555                                         continue;
556                                 if (tTd(30, 3))
557                                         printf("Adding %s: %s\n",
558                                                 hdr, q->q_paddr);
559                                 addheader(hdr, q->q_paddr, &e->e_header);
560                         }
561                 }
562         }
563
564         /* check for message too large */
565         if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
566         {
567                 e->e_flags |= EF_NO_BODY_RETN|EF_CLRQUEUE;
568                 e->e_status = "5.2.3";
569                 usrerr("552 Message exceeds maximum fixed size (%ld)",
570                         MaxMessageSize);
571                 if (LogLevel > 6)
572                         sm_syslog(LOG_NOTICE, e->e_id,
573                                 "message size (%ld) exceeds maximum (%ld)",
574                                 e->e_msgsize, MaxMessageSize);
575         }
576
577         /* check for illegal 8-bit data */
578         if (HasEightBits)
579         {
580                 e->e_flags |= EF_HAS8BIT;
581                 if (!bitset(MM_PASS8BIT|MM_MIME8BIT, MimeMode) &&
582                     !bitset(EF_IS_MIME, e->e_flags))
583                 {
584                         e->e_status = "5.6.1";
585                         usrerr("554 Eight bit data not allowed");
586                 }
587         }
588         else
589         {
590                 /* if it claimed to be 8 bits, well, it lied.... */
591                 if (e->e_bodytype != NULL &&
592                     strcasecmp(e->e_bodytype, "8BITMIME") == 0)
593                         e->e_bodytype = "7BIT";
594         }
595
596         if ((e->e_dfp = fopen(dfname, "r")) == NULL)
597         {
598                 /* we haven't acked receipt yet, so just chuck this */
599                 syserr("Cannot reopen %s", dfname);
600                 finis(TRUE, ExitStat);
601         }
602 }
603
604
605 static void
606 collecttimeout(timeout)
607         time_t timeout;
608 {
609         /* if no progress was made, die now */
610         if (!CollectProgress)
611                 longjmp(CtxCollectTimeout, 1);
612
613         /* otherwise reset the timeout */
614         CollectTimeout = setevent(timeout, collecttimeout, timeout);
615         CollectProgress = FALSE;
616 }
617 \f/*
618 **  TFERROR -- signal error on writing the temporary file.
619 **
620 **      Parameters:
621 **              tf -- the file pointer for the temporary file.
622 **              e -- the current envelope.
623 **
624 **      Returns:
625 **              none.
626 **
627 **      Side Effects:
628 **              Gives an error message.
629 **              Arranges for following output to go elsewhere.
630 */
631
632 void
633 tferror(tf, e)
634         FILE *volatile tf;
635         register ENVELOPE *e;
636 {
637         setstat(EX_IOERR);
638         if (errno == ENOSPC)
639         {
640 #if STAT64 > 0
641                 struct stat64 st;
642 #else
643                 struct stat st;
644 #endif
645                 long avail;
646                 long bsize;
647                 extern long freediskspace __P((char *, long *));
648
649                 e->e_flags |= EF_NO_BODY_RETN;
650
651                 if (
652 #if STAT64 > 0
653                     fstat64(fileno(tf), &st) 
654 #else
655                     fstat(fileno(tf), &st) 
656 #endif
657                     < 0)
658                   st.st_size = 0;
659                 (void) freopen(queuename(e, 'd'), "w", tf);
660                 if (st.st_size <= 0)
661                         fprintf(tf, "\n*** Mail could not be accepted");
662                 else if (sizeof st.st_size > sizeof (long))
663                         fprintf(tf, "\n*** Mail of at least %s bytes could not be accepted\n",
664                                 quad_to_string(st.st_size));
665                 else
666                         fprintf(tf, "\n*** Mail of at least %lu bytes could not be accepted\n",
667                                 (unsigned long) st.st_size);
668                 fprintf(tf, "*** at %s due to lack of disk space for temp file.\n",
669                         MyHostName);
670                 avail = freediskspace(QueueDir, &bsize);
671                 if (avail > 0)
672                 {
673                         if (bsize > 1024)
674                                 avail *= bsize / 1024;
675                         else if (bsize < 1024)
676                                 avail /= 1024 / bsize;
677                         fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n",
678                                 avail);
679                 }
680                 e->e_status = "4.3.1";
681                 usrerr("452 Out of disk space for temp file");
682         }
683         else
684                 syserr("collect: Cannot write tf%s", e->e_id);
685         if (freopen("/dev/null", "w", tf) == NULL)
686                 sm_syslog(LOG_ERR, e->e_id,
687                           "tferror: freopen(\"/dev/null\") failed: %s",
688                           errstring(errno));
689 }
690 \f/*
691 **  EATFROM -- chew up a UNIX style from line and process
692 **
693 **      This does indeed make some assumptions about the format
694 **      of UNIX messages.
695 **
696 **      Parameters:
697 **              fm -- the from line.
698 **
699 **      Returns:
700 **              none.
701 **
702 **      Side Effects:
703 **              extracts what information it can from the header,
704 **              such as the date.
705 */
706
707 # ifndef NOTUNIX
708
709 char    *DowList[] =
710 {
711         "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
712 };
713
714 char    *MonthList[] =
715 {
716         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
717         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
718         NULL
719 };
720
721 void
722 eatfrom(fm, e)
723         char *volatile fm;
724         register ENVELOPE *e;
725 {
726         register char *p;
727         register char **dt;
728
729         if (tTd(30, 2))
730                 printf("eatfrom(%s)\n", fm);
731
732         /* find the date part */
733         p = fm;
734         while (*p != '\0')
735         {
736                 /* skip a word */
737                 while (*p != '\0' && *p != ' ')
738                         p++;
739                 while (*p == ' ')
740                         p++;
741                 if (!(isascii(*p) && isupper(*p)) ||
742                     p[3] != ' ' || p[13] != ':' || p[16] != ':')
743                         continue;
744
745                 /* we have a possible date */
746                 for (dt = DowList; *dt != NULL; dt++)
747                         if (strncmp(*dt, p, 3) == 0)
748                                 break;
749                 if (*dt == NULL)
750                         continue;
751
752                 for (dt = MonthList; *dt != NULL; dt++)
753                         if (strncmp(*dt, &p[4], 3) == 0)
754                                 break;
755                 if (*dt != NULL)
756                         break;
757         }
758
759         if (*p != '\0')
760         {
761                 char *q;
762
763                 /* we have found a date */
764                 q = xalloc(25);
765                 (void) strncpy(q, p, 25);
766                 q[24] = '\0';
767                 q = arpadate(q);
768                 define('a', newstr(q), e);
769         }
770 }
771
772 # endif /* NOTUNIX */