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