(xdialog_show): New function to handle the display of dialog boxes.
[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
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. */
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
fbffe7d9 424 /* Find the last char that fits. */
a492a5b9
RS
425 for (breakpos = s; *breakpos && column < 78; ++breakpos)
426 {
427 if (*breakpos == '\t')
428 column += 8;
429 else
430 column++;
431 }
fbffe7d9
RS
432 /* If we didn't reach end of line, break the line. */
433 if (*breakpos)
a492a5b9 434 {
fbffe7d9
RS
435 /* Back up to just after the last comma that fits. */
436 while (breakpos != s && breakpos[-1] != ',') --breakpos;
437
438 if (breakpos == s)
439 {
440 /* If no comma fits, move past the first address anyway. */
441 while (*breakpos != 0 && *breakpos != ',') ++breakpos;
442 if (*breakpos != 0)
443 /* Include the comma after it. */
444 ++breakpos;
445 }
a492a5b9
RS
446 }
447 /* Output that much, then break the line. */
448 fwrite (s, 1, breakpos - s, rem->handle);
a492a5b9
RS
449 column = 8;
450
451 /* Skip whitespace and prepare to print more addresses. */
452 s = breakpos;
453 while (*s == ' ' || *s == '\t') ++s;
c1380f31
RS
454 if (*s != 0)
455 fputs ("\n\t", rem->handle);
a492a5b9 456 }
6b3ee98a
RS
457 putc ('\n', rem->handle);
458 }
459 return;
460}
461\f
462#define mail_error error
463
464void
465setup_files (the_list, field)
466 register line_list the_list;
467 register char *field;
468{
469 register char *start;
470 register char c;
471 while (true)
472 {
473 while (((c = *field) != '\0') &&
474 ((c == ' ') ||
475 (c == '\t') ||
476 (c == ',')))
477 field += 1;
478 if (c != '\0')
479 {
480 start = field;
481 while (((c = *field) != '\0') &&
482 (c != ' ') &&
483 (c != '\t') &&
484 (c != ','))
485 field += 1;
486 *field = '\0';
487 if (!open_a_file (start))
488 mail_error ("Could not open file %s", start);
489 *field = c;
490 if (c != '\0') continue;
491 }
492 if (the_list == ((line_list) NULL)) return;
493 field = the_list->string;
494 the_list = the_list->continuation;
495 }
496}
497\f
498int
499args_size (the_header)
500 header the_header;
501{
502 register header old = the_header;
503 register line_list rem;
504 register int size = 0;
505 do
506 {
507 char *field;
508 register char *keyword = get_keyword (the_header->text->string, &field);
509 if ((strcmp (keyword, "TO") == 0) ||
510 (strcmp (keyword, "CC") == 0) ||
511 (strcmp (keyword, "BCC") == 0))
512 {
513 size += 1 + strlen (field);
514 for (rem = the_header->text->continuation;
515 rem != NIL;
516 rem = rem->continuation)
517 size += 1 + strlen (rem->string);
518 }
519 the_header = the_header->next;
520 } while (the_header != old);
521 return size;
522}
523
524parse_header (the_header, where)
525 header the_header;
526 register char *where;
527{
528 register header old = the_header;
529 do
530 {
531 char *field;
532 register char *keyword = get_keyword (the_header->text->string, &field);
533 if (strcmp (keyword, "TO") == 0)
534 where = add_field (the_header->text->continuation, field, where);
535 else if (strcmp (keyword, "CC") == 0)
536 where = add_field (the_header->text->continuation, field, where);
537 else if (strcmp (keyword, "BCC") == 0)
538 {
539 where = add_field (the_header->text->continuation, field, where);
540 the_header->previous->next = the_header->next;
541 the_header->next->previous = the_header->previous;
542 }
543 else if (strcmp (keyword, "FCC") == 0)
544 setup_files (the_header->text->continuation, field);
545 the_header = the_header->next;
546 } while (the_header != old);
547 *where = '\0';
548 return;
549}
550\f
551header
552read_header ()
553{
554 register header the_header = ((header) NULL);
555 register line_list *next_line = ((line_list *) NULL);
556
557 init_linebuffer (&lb);
558
559 do
560 {
561 long length;
562 register char *line;
563
564 readline (&lb, stdin);
565 line = lb.buffer;
566 length = strlen (line);
567 if (length == 0) break;
568
569 if (has_keyword (line))
570 {
571 register header old = the_header;
572 the_header = new_header ();
573 if (old == ((header) NULL))
574 {
575 the_header->next = the_header;
576 the_header->previous = the_header;
577 }
578 else
579 {
580 the_header->previous = old;
581 the_header->next = old->next;
582 old->next = the_header;
583 }
584 next_line = &(the_header->text);
585 }
586
587 if (next_line == ((line_list *) NULL))
588 {
589 /* Not a valid header */
590 exit (1);
591 }
592 *next_line = new_list ();
593 (*next_line)->string = alloc_string (length);
594 strcpy (((*next_line)->string), line);
595 next_line = &((*next_line)->continuation);
596 *next_line = NIL;
597
598 } while (true);
599
600 return the_header->next;
601}
602\f
603void
604write_header (the_header)
605 header the_header;
606{
607 register header old = the_header;
608 do
609 {
610 register line_list the_list;
611 for (the_list = the_header->text;
612 the_list != NIL;
613 the_list = the_list->continuation)
614 put_line (the_list->string);
615 the_header = the_header->next;
616 } while (the_header != old);
617 put_line ("");
618 return;
619}
620\f
621void
622main (argc, argv)
623 int argc;
624 char **argv;
625{
626 char *command_line;
627 header the_header;
628 long name_length;
629 char *mail_program_name;
630 char buf[BUFLEN + 1];
631 register int size;
632 FILE *the_pipe;
633
634 extern char *getenv ();
635
636 mail_program_name = getenv ("FAKEMAILER");
637 if (!(mail_program_name && *mail_program_name))
638 mail_program_name = MAIL_PROGRAM_NAME;
639 name_length = strlen (mail_program_name);
640
641 my_name = MY_NAME;
642 the_streams = ((stream_list) NULL);
643 the_date = ((char *) NULL);
644 the_user = ((char *) NULL);
645
646 the_header = read_header ();
647 command_line = alloc_string (name_length + args_size (the_header));
648 strcpy (command_line, mail_program_name);
649 parse_header (the_header, &command_line[name_length]);
650
651 the_pipe = popen (command_line, "w");
652 if (the_pipe == ((FILE *) NULL))
653 fatal ("cannot open pipe to real mailer");
654
655 add_a_stream (the_pipe, pclose);
656
657 write_header (the_header);
658
659 /* Dump the message itself */
660
661 while (!feof (stdin))
662 {
663 size = fread (buf, 1, BUFLEN, stdin);
664 buf[size] = '\0';
665 put_string (buf);
666 }
667
668 exit (close_the_streams ());
669}
670
26528bbc 671#endif /* not MSDOS */
6b3ee98a 672#endif /* not BSD 4.2 (or newer) */