(recover-file): Call auto-save-file-name-p correctly.
[bpt/emacs.git] / lib-src / fakemail.c
CommitLineData
6b3ee98a
RS
1/* sendmail-like interface to /bin/mail for system V,
2 Copyright (C) 1985 Free Software Foundation, Inc.
3
4This file is part of GNU Emacs.
5
518dd722
JB
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 1, or (at your option)
9any later version.
10
6b3ee98a 11GNU Emacs is distributed in the hope that it will be useful,
518dd722
JB
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
6b3ee98a
RS
19
20
21#define NO_SHORTNAMES
18160b98 22#include <../src/config.h>
6b3ee98a
RS
23
24#if defined (BSD) && !defined (BSD4_1) && !defined (USE_FAKEMAIL)
25/* This program isnot used in BSD, so just avoid loader complaints. */
26main ()
27{
28}
29#else /* not BSD 4.2 (or newer) */
26528bbc
RS
30#ifdef MSDOS
31main ()
32{
33}
34#else /* not MSDOS */
6b3ee98a
RS
35/* This conditional contains all the rest of the file. */
36
37/* These are defined in config in some versions. */
38
39#ifdef static
40#undef static
41#endif
42
43#ifdef read
44#undef read
45#undef write
46#undef open
47#undef close
48#endif
49
50#include <stdio.h>
51#include <string.h>
52#include <ctype.h>
53#include <time.h>
54#include <pwd.h>
55\f
56/* Type definitions */
57
58#define boolean int
59#define true 1
60#define false 0
61
62/* Various lists */
63
64struct line_record
65{
66 char *string;
67 struct line_record *continuation;
68};
69typedef struct line_record *line_list;
70
71struct header_record
72{
73 line_list text;
74 struct header_record *next;
75 struct header_record *previous;
76};
77typedef struct header_record *header;
78
79struct stream_record
80{
81 FILE *handle;
82 int (*action)();
83 struct stream_record *rest_streams;
84};
85typedef struct stream_record *stream_list;
86
87/* A `struct linebuffer' is a structure which holds a line of text.
88 * `readline' reads a line from a stream into a linebuffer
89 * and works regardless of the length of the line.
90 */
91
92struct linebuffer
93{
94 long size;
95 char *buffer;
96};
97
98struct linebuffer lb;
99
100#define new_list() \
101 ((line_list) xmalloc (sizeof (struct line_record)))
102#define new_header() \
103 ((header) xmalloc (sizeof (struct header_record)))
104#define new_stream() \
105 ((stream_list) xmalloc (sizeof (struct stream_record)))
106#define alloc_string(nchars) \
107 ((char *) xmalloc ((nchars) + 1))
108\f
109/* Global declarations */
110
111#define BUFLEN 1024
112#define KEYWORD_SIZE 256
113#define FROM_PREFIX "From"
114#define MY_NAME "fakemail"
115#define NIL ((line_list) NULL)
116#define INITIAL_LINE_SIZE 200
117
118#ifndef MAIL_PROGRAM_NAME
119#define MAIL_PROGRAM_NAME "/bin/mail"
120#endif
121
122static char *my_name;
123static char *the_date;
124static char *the_user;
125static line_list file_preface;
126static stream_list the_streams;
127static boolean no_problems = true;
128
129extern FILE *popen ();
130extern int fclose (), pclose ();
131extern char *malloc (), *realloc ();
132
133#ifdef CURRENT_USER
134extern struct passwd *getpwuid ();
135extern unsigned short geteuid ();
136static struct passwd *my_entry;
137#define cuserid(s) \
138(my_entry = getpwuid (((int) geteuid ())), \
139 my_entry->pw_name)
140#endif
141\f
142/* Utilities */
143
144/* Print error message. `s1' is printf control string, `s2' is arg for it. */
145
146static void
147error (s1, s2)
148 char *s1, *s2;
149{
150 printf ("%s: ", my_name);
151 printf (s1, s2);
152 printf ("\n");
153 no_problems = false;
154}
155
156/* Print error message and exit. */
157
158static void
159fatal (s1, s2)
160 char *s1, *s2;
161{
162 error (s1, s2);
163 exit (1);
164}
165
166/* Like malloc but get fatal error if memory is exhausted. */
167
168static char *
169xmalloc (size)
170 int size;
171{
172 char *result = malloc (((unsigned) size));
173 if (result == ((char *) NULL))
174 fatal ("virtual memory exhausted", 0);
175 return result;
176}
177
178static char *
179xrealloc (ptr, size)
180 char *ptr;
181 int size;
182{
183 char *result = realloc (ptr, ((unsigned) size));
184 if (result == ((char *) NULL))
185 fatal ("virtual memory exhausted");
186 return result;
187}
188\f
189/* Initialize a linebuffer for use */
190
191void
192init_linebuffer (linebuffer)
193 struct linebuffer *linebuffer;
194{
195 linebuffer->size = INITIAL_LINE_SIZE;
196 linebuffer->buffer = ((char *) xmalloc (INITIAL_LINE_SIZE));
197}
198
199/* Read a line of text from `stream' into `linebuffer'.
200 * Return the length of the line.
201 */
202
203long
204readline (linebuffer, stream)
205 struct linebuffer *linebuffer;
206 FILE *stream;
207{
208 char *buffer = linebuffer->buffer;
209 char *p = linebuffer->buffer;
210 char *end = p + linebuffer->size;
211
212 while (true)
213 {
214 int c = getc (stream);
215 if (p == end)
216 {
217 linebuffer->size *= 2;
218 buffer = ((char *) xrealloc (buffer, linebuffer->size));
219 p += buffer - linebuffer->buffer;
220 end += buffer - linebuffer->buffer;
221 linebuffer->buffer = buffer;
222 }
223 if (c < 0 || c == '\n')
224 {
225 *p = 0;
226 break;
227 }
228 *p++ = c;
229 }
230
231 return p - buffer;
232}
233\f
234char *
235get_keyword (field, rest)
236 register char *field;
237 char **rest;
238{
239 static char keyword[KEYWORD_SIZE];
240 register char *ptr;
241 register char c;
242
243 ptr = &keyword[0];
244 c = *field++;
245 if ((isspace (c)) || (c == ':'))
246 return ((char *) NULL);
247 *ptr++ = ((islower (c)) ? (toupper (c)) : c);
248 while (((c = *field++) != ':') && (!(isspace (c))))
249 *ptr++ = ((islower (c)) ? (toupper (c)) : c);
250 *ptr++ = '\0';
251 while (isspace (c)) c = *field++;
252 if (c != ':') return ((char *) NULL);
253 *rest = field;
254 return &keyword[0];
255}
256
257boolean
258has_keyword (field)
259 char *field;
260{
261 char *ignored;
262 return (get_keyword (field, &ignored) != ((char *) NULL));
263}
264
265char *
266add_field (the_list, field, where)
267 line_list the_list;
268 register char *field, *where;
269{
270 register char c;
271 while (true)
272 {
273 *where++ = ' ';
274 while ((c = *field++) != '\0')
275 {
276 if (c == '(')
277 {
278 while (*field && *field != ')') ++field;
279 if (! (*field++)) break; /* no closer */
280 if (! (*field)) break; /* closerNULL */
281 c = *field;
282 }
283 *where++ = ((c == ','||c=='>'||c=='<') ? ' ' : c);
284 }
285 if (the_list == NIL) break;
286 field = the_list->string;
287 the_list = the_list->continuation;
288 }
289 return where;
290}
291\f
292line_list
293make_file_preface ()
294{
295 char *the_string, *temp;
296 long idiotic_interface;
297 long prefix_length;
298 long user_length;
299 long date_length;
300 line_list result;
301
302 prefix_length = strlen (FROM_PREFIX);
303 time (&idiotic_interface);
304 the_date = ctime (&idiotic_interface);
305 /* the_date has an unwanted newline at the end */
306 date_length = strlen (the_date) - 1;
307 the_date[date_length] = '\0';
308 temp = cuserid ((char *) NULL);
309 user_length = strlen (temp);
310 the_user = alloc_string (user_length + 1);
311 strcpy (the_user, temp);
312 the_string = alloc_string (3 + prefix_length +
313 user_length +
314 date_length);
315 temp = the_string;
316 strcpy (temp, FROM_PREFIX);
317 temp = &temp[prefix_length];
318 *temp++ = ' ';
319 strcpy (temp, the_user);
320 temp = &temp[user_length];
321 *temp++ = ' ';
322 strcpy (temp, the_date);
323 result = new_list ();
324 result->string = the_string;
325 result->continuation = ((line_list) NULL);
326 return result;
327}
328
329void
330write_line_list (the_list, the_stream)
331 register line_list the_list;
332 FILE *the_stream;
333{
334 for ( ;
335 the_list != ((line_list) NULL) ;
336 the_list = the_list->continuation)
337 {
338 fputs (the_list->string, the_stream);
339 putc ('\n', the_stream);
340 }
341 return;
342}
343\f
344int
345close_the_streams ()
346{
347 register stream_list rem;
348 for (rem = the_streams;
349 rem != ((stream_list) NULL);
350 rem = rem->rest_streams)
351 no_problems = (no_problems &&
352 ((*rem->action) (rem->handle) == 0));
353 the_streams = ((stream_list) NULL);
354 return (no_problems ? 0 : 1);
355}
356
357void
358add_a_stream (the_stream, closing_action)
359 FILE *the_stream;
360 int (*closing_action)();
361{
362 stream_list old = the_streams;
363 the_streams = new_stream ();
364 the_streams->handle = the_stream;
365 the_streams->action = closing_action;
366 the_streams->rest_streams = old;
367 return;
368}
369
370int
371my_fclose (the_file)
372 FILE *the_file;
373{
374 putc ('\n', the_file);
375 fflush (the_file);
376 return fclose (the_file);
377}
378
379boolean
380open_a_file (name)
381 char *name;
382{
383 FILE *the_stream = fopen (name, "a");
384 if (the_stream != ((FILE *) NULL))
385 {
386 add_a_stream (the_stream, my_fclose);
387 if (the_user == ((char *) NULL))
388 file_preface = make_file_preface ();
389 write_line_list (file_preface, the_stream);
390 return true;
391 }
392 return false;
393}
394
395void
396put_string (s)
397 char *s;
398{
399 register stream_list rem;
400 for (rem = the_streams;
401 rem != ((stream_list) NULL);
402 rem = rem->rest_streams)
403 fputs (s, rem->handle);
404 return;
405}
406
407void
a492a5b9
RS
408put_line (string)
409 char *string;
6b3ee98a
RS
410{
411 register stream_list rem;
412 for (rem = the_streams;
413 rem != ((stream_list) NULL);
414 rem = rem->rest_streams)
415 {
a492a5b9
RS
416 char *s = string;
417 int column = 0;
418
419 /* Divide STRING into lines. */
420 while (*s != 0)
421 {
422 char *breakpos;
423
424 /* Find the last char that fits. */
425 for (breakpos = s; *breakpos && column < 78; ++breakpos)
426 {
427 if (*breakpos == '\t')
428 column += 8;
429 else
430 column++;
431 }
432 /* Back up to just after the last comma that fits. */
433 while (breakpos != s && breakpos[-1] != ',') --breakpos;
434 if (breakpos == s)
435 {
436 /* If no comma fits, move past the first address anyway. */
437 while (*breakpos != 0 && *breakpos != ',') ++breakpos;
438 if (*breakpos != 0)
439 /* Include the comma after it. */
440 ++breakpos;
441 }
442 /* Output that much, then break the line. */
443 fwrite (s, 1, breakpos - s, rem->handle);
a492a5b9
RS
444 column = 8;
445
446 /* Skip whitespace and prepare to print more addresses. */
447 s = breakpos;
448 while (*s == ' ' || *s == '\t') ++s;
c1380f31
RS
449 if (*s != 0)
450 fputs ("\n\t", rem->handle);
a492a5b9 451 }
6b3ee98a
RS
452 putc ('\n', rem->handle);
453 }
454 return;
455}
456\f
457#define mail_error error
458
459void
460setup_files (the_list, field)
461 register line_list the_list;
462 register char *field;
463{
464 register char *start;
465 register char c;
466 while (true)
467 {
468 while (((c = *field) != '\0') &&
469 ((c == ' ') ||
470 (c == '\t') ||
471 (c == ',')))
472 field += 1;
473 if (c != '\0')
474 {
475 start = field;
476 while (((c = *field) != '\0') &&
477 (c != ' ') &&
478 (c != '\t') &&
479 (c != ','))
480 field += 1;
481 *field = '\0';
482 if (!open_a_file (start))
483 mail_error ("Could not open file %s", start);
484 *field = c;
485 if (c != '\0') continue;
486 }
487 if (the_list == ((line_list) NULL)) return;
488 field = the_list->string;
489 the_list = the_list->continuation;
490 }
491}
492\f
493int
494args_size (the_header)
495 header the_header;
496{
497 register header old = the_header;
498 register line_list rem;
499 register int size = 0;
500 do
501 {
502 char *field;
503 register char *keyword = get_keyword (the_header->text->string, &field);
504 if ((strcmp (keyword, "TO") == 0) ||
505 (strcmp (keyword, "CC") == 0) ||
506 (strcmp (keyword, "BCC") == 0))
507 {
508 size += 1 + strlen (field);
509 for (rem = the_header->text->continuation;
510 rem != NIL;
511 rem = rem->continuation)
512 size += 1 + strlen (rem->string);
513 }
514 the_header = the_header->next;
515 } while (the_header != old);
516 return size;
517}
518
519parse_header (the_header, where)
520 header the_header;
521 register char *where;
522{
523 register header old = the_header;
524 do
525 {
526 char *field;
527 register char *keyword = get_keyword (the_header->text->string, &field);
528 if (strcmp (keyword, "TO") == 0)
529 where = add_field (the_header->text->continuation, field, where);
530 else if (strcmp (keyword, "CC") == 0)
531 where = add_field (the_header->text->continuation, field, where);
532 else if (strcmp (keyword, "BCC") == 0)
533 {
534 where = add_field (the_header->text->continuation, field, where);
535 the_header->previous->next = the_header->next;
536 the_header->next->previous = the_header->previous;
537 }
538 else if (strcmp (keyword, "FCC") == 0)
539 setup_files (the_header->text->continuation, field);
540 the_header = the_header->next;
541 } while (the_header != old);
542 *where = '\0';
543 return;
544}
545\f
546header
547read_header ()
548{
549 register header the_header = ((header) NULL);
550 register line_list *next_line = ((line_list *) NULL);
551
552 init_linebuffer (&lb);
553
554 do
555 {
556 long length;
557 register char *line;
558
559 readline (&lb, stdin);
560 line = lb.buffer;
561 length = strlen (line);
562 if (length == 0) break;
563
564 if (has_keyword (line))
565 {
566 register header old = the_header;
567 the_header = new_header ();
568 if (old == ((header) NULL))
569 {
570 the_header->next = the_header;
571 the_header->previous = the_header;
572 }
573 else
574 {
575 the_header->previous = old;
576 the_header->next = old->next;
577 old->next = the_header;
578 }
579 next_line = &(the_header->text);
580 }
581
582 if (next_line == ((line_list *) NULL))
583 {
584 /* Not a valid header */
585 exit (1);
586 }
587 *next_line = new_list ();
588 (*next_line)->string = alloc_string (length);
589 strcpy (((*next_line)->string), line);
590 next_line = &((*next_line)->continuation);
591 *next_line = NIL;
592
593 } while (true);
594
595 return the_header->next;
596}
597\f
598void
599write_header (the_header)
600 header the_header;
601{
602 register header old = the_header;
603 do
604 {
605 register line_list the_list;
606 for (the_list = the_header->text;
607 the_list != NIL;
608 the_list = the_list->continuation)
609 put_line (the_list->string);
610 the_header = the_header->next;
611 } while (the_header != old);
612 put_line ("");
613 return;
614}
615\f
616void
617main (argc, argv)
618 int argc;
619 char **argv;
620{
621 char *command_line;
622 header the_header;
623 long name_length;
624 char *mail_program_name;
625 char buf[BUFLEN + 1];
626 register int size;
627 FILE *the_pipe;
628
629 extern char *getenv ();
630
631 mail_program_name = getenv ("FAKEMAILER");
632 if (!(mail_program_name && *mail_program_name))
633 mail_program_name = MAIL_PROGRAM_NAME;
634 name_length = strlen (mail_program_name);
635
636 my_name = MY_NAME;
637 the_streams = ((stream_list) NULL);
638 the_date = ((char *) NULL);
639 the_user = ((char *) NULL);
640
641 the_header = read_header ();
642 command_line = alloc_string (name_length + args_size (the_header));
643 strcpy (command_line, mail_program_name);
644 parse_header (the_header, &command_line[name_length]);
645
646 the_pipe = popen (command_line, "w");
647 if (the_pipe == ((FILE *) NULL))
648 fatal ("cannot open pipe to real mailer");
649
650 add_a_stream (the_pipe, pclose);
651
652 write_header (the_header);
653
654 /* Dump the message itself */
655
656 while (!feof (stdin))
657 {
658 size = fread (buf, 1, BUFLEN, stdin);
659 buf[size] = '\0';
660 put_string (buf);
661 }
662
663 exit (close_the_streams ());
664}
665
26528bbc 666#endif /* not MSDOS */
6b3ee98a 667#endif /* not BSD 4.2 (or newer) */