fix segfault in prompter
[mmh] / uip / prompter.c
1 /*
2 ** prompter.c -- simple prompting editor front-end
3 **
4 ** This code is Copyright (c) 2002, by the authors of nmh.  See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
7 */
8
9 #include <h/mh.h>
10 #include <fcntl.h>
11 #include <h/signals.h>
12 #include <errno.h>
13 #include <signal.h>
14 #include <setjmp.h>
15 #include <unistd.h>
16 #include <ctype.h>
17 #include <sys/stat.h>
18 #include <locale.h>
19 #include <sysexits.h>
20
21 static struct swit switches[] = {
22 #define PREPSW  0
23         { "prepend", 0 },
24 #define NPREPSW  1
25         { "noprepend", 2 },
26 #define RAPDSW  2
27         { "rapid", 0 },
28 #define NRAPDSW  3
29         { "norapid", 2 },
30 #define BODYSW  4
31         { "body", 0 },
32 #define NBODYSW  5
33         { "nobody", 2 },
34 #define VERSIONSW 6
35         { "Version", 0 },
36 #define HELPSW  7
37         { "help", 0 },
38         { NULL, 0 }
39 };
40
41
42 volatile sig_atomic_t wtuser = 0;
43 volatile sig_atomic_t sigint = 0;
44 static jmp_buf sigenv;
45
46 /*
47 ** prototypes
48 */
49 int getln(char *, int);
50 static void intrser(int);
51
52
53 int
54 main(int argc, char **argv)
55 {
56         int qbody = 1, prepend = 1, rapid = 0;
57         int fdi, fdo, i;
58         char *cp, *drft = NULL;
59         enum state state;
60         struct field f = free_field;
61         char buffer[BUFSIZ], tmpfil[BUFSIZ];
62         char **arguments, **argp;
63         FILE *in, *out;
64         char *tfile = NULL;
65
66         setlocale(LC_ALL, "");
67         invo_name = mhbasename(argv[0]);
68
69         /* read user profile/context */
70         context_read();
71
72         arguments = getarguments(invo_name, argc, argv, 1);
73         argp = arguments;
74
75         while ((cp = *argp++)) {
76                 if (*cp == '-') {
77                         switch (smatch(++cp, switches)) {
78                         case AMBIGSW:
79                                 ambigsw(cp, switches);
80                                 exit(EX_USAGE);
81                         case UNKWNSW:
82                                 adios(EX_USAGE, NULL, "-%s unknown", cp);
83
84                         case HELPSW:
85                                 snprintf(buffer, sizeof(buffer),
86                                                 "%s [switches] file",
87                                                 invo_name);
88                                 print_help(buffer, switches, 1);
89                                 exit(argc == 2 ? EX_OK : EX_USAGE);
90
91                         case VERSIONSW:
92                                 print_version(invo_name);
93                                 exit(argc == 2 ? EX_OK : EX_USAGE);
94
95                         case PREPSW:
96                                 prepend++;
97                                 continue;
98                         case NPREPSW:
99                                 prepend = 0;
100                                 continue;
101
102                         case RAPDSW:
103                                 rapid++;
104                                 continue;
105                         case NRAPDSW:
106                                 rapid = 0;
107                                 continue;
108
109                         case BODYSW:
110                                 qbody++;
111                                 continue;
112                         case NBODYSW:
113                                 qbody = 0;
114                                 continue;
115                         }
116                 } else if (!drft) {
117                         drft = cp;
118                 }
119         }
120
121         if (!drft) {
122                 adios(EX_USAGE, NULL, "usage: %s [switches] file", invo_name);
123         }
124         if ((in = fopen(drft, "r")) == NULL) {
125                 adios(EX_IOERR, drft, "unable to open");
126         }
127
128         tfile = m_mktemp2(NULL, invo_name, NULL, &out);
129         if (tfile == NULL) {
130                 adios(EX_CANTCREAT, "prompter", "unable to create temporary file");
131         }
132         chmod(tmpfil, 0600);
133         strncpy(tmpfil, tfile, sizeof(tmpfil));
134
135         sigint = 0;
136         SIGNAL2(SIGINT, intrser);
137
138         /*
139         ** Loop through the lines of the draft skeleton.
140         */
141         for (state = FLD2;;) {
142                 switch (state = m_getfld2(state, &f, in)) {
143                 case FLD2:
144                         /*
145                         ** Check if the value of field contains
146                         ** anything other than space or tab.
147                         */
148                         for (cp = f.value; *cp; cp++) {
149                                 if (*cp != ' ' && *cp != '\t') {
150                                         break;
151                                 }
152                         }
153
154                         /* If so, just add header line to draft */
155                         if (*cp++ != '\n' || *cp) {
156                                 printf("%s:%s", f.name, f.value);
157                                 fprintf(out, "%s:%s", f.name, f.value);
158                         } else {
159                                 /* Else, get value of header field */
160                                 printf("%s: ", f.name);
161                                 fflush(stdout);
162                                 i = getln(buffer, sizeof(buffer));
163                                 if (i == -1) {
164 abort:
165                                         unlink(tmpfil);
166                                         exit(EX_DATAERR);
167                                 }
168                                 if (i || (buffer[0]!='\n' && buffer[0]!='\0')) {
169                                         fprintf(out, "%s:", f.name);
170                                         do {
171                                                 if (buffer[0] != ' ' && buffer[0] != '\t') {
172                                                         putc(' ', out);
173                                                 }
174                                                 fprintf(out, "%s", buffer);
175                                         } while (i == 1 && (i = getln(buffer, sizeof(buffer))) >= 0);
176                                         if (i == -1) {
177                                                 goto abort;
178                                         }
179                                 }
180                         }
181                         continue;
182
183                 case BODY2:
184                 case FILEEOF2:
185                         fprintf(out, "--------\n");
186                         if (qbody) {
187                                 if (f.value == NULL) {
188                                         printf("--------\n");
189                                         goto has_no_body;
190                                 }
191
192                                 if (prepend) {
193                                         printf("--------Enter initial text\n");
194                                         fflush(stdout);
195                                         for (;;) {
196                                                 getln(buffer, sizeof(buffer));
197                                                 if (!*buffer) {
198                                                         break;
199                                                 }
200                                                 fprintf(out, "%s", buffer);
201                                         }
202                                 } else {
203                                         printf("--------\n");
204                                 }
205                         }
206
207                         if (state == BODY2) {
208                                 do {
209                                         fprintf(out, "%s", f.value);
210                                         if (!rapid && !sigint) {
211                                                 printf("%s", f.value);
212                                         }
213                                 } while ((state = m_getfld2(state, &f, in))
214                                                 ==BODY2);
215                         }
216
217                         if (prepend || !qbody) {
218                                 break;
219                         }
220
221                         printf("--------Enter additional text\n");
222 has_no_body:
223                         fflush(stdout);
224                         for (;;) {
225                                 getln(buffer, sizeof(buffer));
226                                 if (!*buffer) {
227                                         break;
228                                 }
229                                 fprintf(out, "%s", buffer);
230                         }
231                         break;
232
233                 default:
234                         adios(EX_DATAERR, NULL, "skeleton is poorly formatted");
235                 }
236                 break;
237         }
238
239         if (qbody) {
240                 printf("--------\n");
241         }
242
243         fflush(stdout);
244         fclose(in);
245         fclose(out);
246         SIGNAL(SIGINT, SIG_IGN);
247
248         if ((fdi = open(tmpfil, O_RDONLY)) == NOTOK) {
249                 adios(EX_IOERR, tmpfil, "unable to re-open");
250         }
251         if ((fdo = creat(drft, m_gmprot())) == NOTOK) {
252                 adios(EX_IOERR, drft, "unable to write");
253         }
254         cpydata(fdi, fdo, tmpfil, drft);
255         close(fdi);
256         close(fdo);
257         unlink(tmpfil);
258
259         context_save();
260         return EX_OK;
261 }
262
263
264 int
265 getln(char *buffer, int n)
266 {
267         int c;
268         sig_atomic_t psigint = sigint;
269         char *cp;
270
271         cp = buffer;
272         *cp = '\0';
273
274         switch (setjmp(sigenv)) {
275         case 0:
276                 wtuser = 1;
277                 break;
278
279         default:
280                 wtuser = 0;
281                 if (sigint == psigint) {
282                         return 0;
283                 } else {
284                         sigint = psigint;
285                         return NOTOK;
286                 }
287         }
288
289         for (;;) {
290                 switch (c = getchar()) {
291                 case EOF:
292                         clearerr(stdin);
293                         longjmp(sigenv, DONE);
294
295                 case '\n':
296                         if (cp[-1] == '\\') {
297                                 cp[-1] = c;
298                                 wtuser = 0;
299                                 return 1;
300                         }
301                         *cp++ = c;
302                         *cp = '\0';
303                         wtuser = 0;
304                         return 0;
305
306                 default:
307                         if (cp < buffer + n)
308                                 *cp++ = c;
309                         *cp = '\0';
310                 }
311         }
312 }
313
314
315 static void
316 intrser(int i)
317 {
318         if (wtuser) {
319                 close(STDIN_FILENO);
320         }
321         sigint++;
322 }