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