remove longjmp in signalhandler of 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
17 static struct swit switches[] = {
18 #define PREPSW  0
19         { "prepend", 0 },
20 #define NPREPSW  1
21         { "noprepend", 2 },
22 #define RAPDSW  2
23         { "rapid", 0 },
24 #define NRAPDSW  3
25         { "norapid", 2 },
26 #define BODYSW  4
27         { "body", 0 },
28 #define NBODYSW  5
29         { "nobody", 2 },
30 #define VERSIONSW 6
31         { "Version", 0 },
32 #define HELPSW  7
33         { "help", 0 },
34         { NULL, 0 }
35 };
36
37
38 volatile sig_atomic_t wtuser = 0;
39 volatile sig_atomic_t sigint = 0;
40 static jmp_buf sigenv;
41
42 /*
43 ** prototypes
44 */
45 int getln(char *, int);
46 static void intrser(int);
47
48
49 int
50 main(int argc, char **argv)
51 {
52         int qbody = 1, prepend = 1, rapid = 0;
53         int fdi, fdo, i, state;
54         char *cp, *drft = NULL;
55         char name[NAMESZ], field[BUFSIZ];
56         char buffer[BUFSIZ], tmpfil[BUFSIZ];
57         char **arguments, **argp;
58         FILE *in, *out;
59         char *tfile = NULL;
60
61         setlocale(LC_ALL, "");
62         invo_name = mhbasename(argv[0]);
63
64         /* read user profile/context */
65         context_read();
66
67         arguments = getarguments(invo_name, argc, argv, 1);
68         argp = arguments;
69
70         while ((cp = *argp++)) {
71                 if (*cp == '-') {
72                         switch (smatch(++cp, switches)) {
73                         case AMBIGSW:
74                                 ambigsw(cp, switches);
75                                 exit(1);
76                         case UNKWNSW:
77                                 adios(NULL, "-%s unknown", cp);
78
79                         case HELPSW:
80                                 snprintf(buffer, sizeof(buffer),
81                                                 "%s [switches] file",
82                                                 invo_name);
83                                 print_help(buffer, switches, 1);
84                                 exit(0);
85                         case VERSIONSW:
86                                 print_version(invo_name);
87                                 exit(0);
88
89                         case PREPSW:
90                                 prepend++;
91                                 continue;
92                         case NPREPSW:
93                                 prepend = 0;
94                                 continue;
95
96                         case RAPDSW:
97                                 rapid++;
98                                 continue;
99                         case NRAPDSW:
100                                 rapid = 0;
101                                 continue;
102
103                         case BODYSW:
104                                 qbody++;
105                                 continue;
106                         case NBODYSW:
107                                 qbody = 0;
108                                 continue;
109                         }
110                 } else if (!drft) {
111                         drft = cp;
112                 }
113         }
114
115         if (!drft)
116                 adios(NULL, "usage: %s [switches] file", invo_name);
117         if ((in = fopen(drft, "r")) == NULL)
118                 adios(drft, "unable to open");
119
120         tfile = m_mktemp2(NULL, invo_name, NULL, &out);
121         if (tfile == NULL)
122                 adios("prompter", "unable to create temporary file");
123         chmod(tmpfil, 0600);
124         strncpy(tmpfil, tfile, sizeof(tmpfil));
125
126         sigint = 0;
127         SIGNAL2(SIGINT, intrser);
128
129         /*
130         ** Loop through the lines of the draft skeleton.
131         */
132         for (state = FLD;;) {
133                 switch (state = m_getfld(state, name, field, sizeof(field),
134                                 in)) {
135                 case FLD:
136                 case FLDEOF:
137                 case FLDPLUS:
138                         /*
139                         ** Check if the value of field contains
140                         ** anything other than space or tab.
141                         */
142                         for (cp = field; *cp; cp++)
143                                 if (*cp != ' ' && *cp != '\t')
144                                         break;
145
146                         /* If so, just add header line to draft */
147                         if (*cp++ != '\n' || *cp) {
148                                 printf("%s:%s", name, field);
149                                 fprintf(out, "%s:%s", name, field);
150                                 while (state == FLDPLUS) {
151                                         state = m_getfld(state, name, field,
152                                                         sizeof(field), in);
153                                         printf("%s", field);
154                                         fprintf(out, "%s", field);
155                                 }
156                         } else {
157                                 /* Else, get value of header field */
158                                 printf("%s: ", name);
159                                 fflush(stdout);
160                                 i = getln(field, sizeof(field));
161                                 if (i == -1) {
162 abort:
163                                         unlink(tmpfil);
164                                         /* sysexits.h EX_DATAERR */
165                                         exit(1);
166                                 }
167                                 if (i || (field[0]!='\n' && field[0]!='\0')) {
168                                         fprintf(out, "%s:", name);
169                                         do {
170                                                 if (field[0] != ' ' && field[0] != '\t')
171                                                         putc(' ', out);
172                                                 fprintf(out, "%s", field);
173                                         } while (i == 1 && (i = getln(field, sizeof(field))) >= 0);
174                                         if (i == -1)
175                                                 goto abort;
176                                 }
177                         }
178
179                         if (state == FLDEOF) {  /* moby hack */
180                                 /* draft has no body separator; only headers */
181                                 fprintf(out, "--------\n");
182                                 if (!qbody)
183                                         break;
184                                 printf("--------\n");
185                                 goto has_no_body;
186                         }
187                         continue;
188
189                 case BODY:
190                 case BODYEOF:
191                 case FILEEOF:
192                         fprintf(out, "--------\n");
193                         if (qbody) {
194                                 if (!*field) {
195                                         printf("--------\n");
196                                         goto has_no_body;
197                                 }
198
199                                 if (prepend) {
200                                         printf("--------Enter initial text\n");
201                                         fflush(stdout);
202                                         for (;;) {
203                                                 getln(buffer, sizeof(buffer));
204                                                 if (!*buffer)
205                                                         break;
206                                                 fprintf(out, "%s", buffer);
207                                         }
208                                 } else {
209                                         printf("--------\n");
210                                 }
211                         }
212
213                         do {
214                                 fprintf(out, "%s", field);
215                                 if (!rapid && !sigint)
216                                         printf("%s", field);
217                         } while (state == BODY &&
218                                         (state = m_getfld(state, name,
219                                         field, sizeof(field), in)));
220
221                         if (prepend || !qbody)
222                                 break;
223
224                         printf("--------Enter additional text\n");
225 has_no_body:
226                         fflush(stdout);
227                         for (;;) {
228                                 getln(field, sizeof(field));
229                                 if (!*field)
230                                         break;
231                                 fprintf(out, "%s", field);
232                         }
233                         break;
234
235                 default:
236                         adios(NULL, "skeleton is poorly formatted");
237                 }
238                 break;
239         }
240
241         if (qbody)
242                 printf("--------\n");
243
244         fflush(stdout);
245         fclose(in);
246         fclose(out);
247         SIGNAL(SIGINT, SIG_IGN);
248
249         if ((fdi = open(tmpfil, O_RDONLY)) == NOTOK)
250                 adios(tmpfil, "unable to re-open");
251         if ((fdo = creat(drft, m_gmprot())) == NOTOK)
252                 adios(drft, "unable to write");
253         cpydata(fdi, fdo, tmpfil, drft);
254         close(fdi);
255         close(fdo);
256         unlink(tmpfil);
257
258         context_save();  /* save the context file */
259         return 0;
260 }
261
262
263 int
264 getln(char *buffer, int n)
265 {
266         int c;
267         sig_atomic_t psigint;
268         char *cp;
269
270         cp = buffer;
271         *cp = '\0';
272
273         switch (setjmp(sigenv)) {
274         case 0:
275                 wtuser = 1;
276                 psigint = sigint;
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 }