set psigint befor the setjmp 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, state;
58         char *cp, *drft = NULL;
59         char name[NAMESZ], field[BUFSIZ];
60         char buffer[BUFSIZ], tmpfil[BUFSIZ];
61         char **arguments, **argp;
62         FILE *in, *out;
63         char *tfile = NULL;
64
65         setlocale(LC_ALL, "");
66         invo_name = mhbasename(argv[0]);
67
68         /* read user profile/context */
69         context_read();
70
71         arguments = getarguments(invo_name, argc, argv, 1);
72         argp = arguments;
73
74         while ((cp = *argp++)) {
75                 if (*cp == '-') {
76                         switch (smatch(++cp, switches)) {
77                         case AMBIGSW:
78                                 ambigsw(cp, switches);
79                                 exit(EX_USAGE);
80                         case UNKWNSW:
81                                 adios(EX_USAGE, NULL, "-%s unknown", cp);
82
83                         case HELPSW:
84                                 snprintf(buffer, sizeof(buffer),
85                                                 "%s [switches] file",
86                                                 invo_name);
87                                 print_help(buffer, switches, 1);
88                                 exit(argc == 2 ? EX_OK : EX_USAGE);
89
90                         case VERSIONSW:
91                                 print_version(invo_name);
92                                 exit(argc == 2 ? EX_OK : EX_USAGE);
93
94                         case PREPSW:
95                                 prepend++;
96                                 continue;
97                         case NPREPSW:
98                                 prepend = 0;
99                                 continue;
100
101                         case RAPDSW:
102                                 rapid++;
103                                 continue;
104                         case NRAPDSW:
105                                 rapid = 0;
106                                 continue;
107
108                         case BODYSW:
109                                 qbody++;
110                                 continue;
111                         case NBODYSW:
112                                 qbody = 0;
113                                 continue;
114                         }
115                 } else if (!drft) {
116                         drft = cp;
117                 }
118         }
119
120         if (!drft) {
121                 adios(EX_USAGE, NULL, "usage: %s [switches] file", invo_name);
122         }
123         if ((in = fopen(drft, "r")) == NULL) {
124                 adios(EX_IOERR, drft, "unable to open");
125         }
126
127         tfile = m_mktemp2(NULL, invo_name, NULL, &out);
128         if (tfile == NULL) {
129                 adios(EX_CANTCREAT, "prompter", "unable to create temporary file");
130         }
131         chmod(tmpfil, 0600);
132         strncpy(tmpfil, tfile, sizeof(tmpfil));
133
134         sigint = 0;
135         SIGNAL2(SIGINT, intrser);
136
137         /*
138         ** Loop through the lines of the draft skeleton.
139         */
140         for (state = FLD;;) {
141                 switch (state = m_getfld(state, name, field, sizeof(field),
142                                 in)) {
143                 case FLD:
144                 case FLDEOF:
145                 case FLDPLUS:
146                         /*
147                         ** Check if the value of field contains
148                         ** anything other than space or tab.
149                         */
150                         for (cp = field; *cp; cp++)
151                                 if (*cp != ' ' && *cp != '\t')
152                                         break;
153
154                         /* If so, just add header line to draft */
155                         if (*cp++ != '\n' || *cp) {
156                                 printf("%s:%s", name, field);
157                                 fprintf(out, "%s:%s", name, field);
158                                 while (state == FLDPLUS) {
159                                         state = m_getfld(state, name, field,
160                                                         sizeof(field), in);
161                                         printf("%s", field);
162                                         fprintf(out, "%s", field);
163                                 }
164                         } else {
165                                 /* Else, get value of header field */
166                                 printf("%s: ", name);
167                                 fflush(stdout);
168                                 i = getln(field, sizeof(field));
169                                 if (i == -1) {
170 abort:
171                                         unlink(tmpfil);
172                                         exit(EX_DATAERR);
173                                 }
174                                 if (i || (field[0]!='\n' && field[0]!='\0')) {
175                                         fprintf(out, "%s:", name);
176                                         do {
177                                                 if (field[0] != ' ' && field[0] != '\t')
178                                                         putc(' ', out);
179                                                 fprintf(out, "%s", field);
180                                         } while (i == 1 && (i = getln(field, sizeof(field))) >= 0);
181                                         if (i == -1)
182                                                 goto abort;
183                                 }
184                         }
185
186                         if (state == FLDEOF) {  /* moby hack */
187                                 /* draft has no body separator; only headers */
188                                 fprintf(out, "--------\n");
189                                 if (!qbody)
190                                         break;
191                                 printf("--------\n");
192                                 goto has_no_body;
193                         }
194                         continue;
195
196                 case BODY:
197                 case BODYEOF:
198                 case FILEEOF:
199                         fprintf(out, "--------\n");
200                         if (qbody) {
201                                 if (!*field) {
202                                         printf("--------\n");
203                                         goto has_no_body;
204                                 }
205
206                                 if (prepend) {
207                                         printf("--------Enter initial text\n");
208                                         fflush(stdout);
209                                         for (;;) {
210                                                 getln(buffer, sizeof(buffer));
211                                                 if (!*buffer)
212                                                         break;
213                                                 fprintf(out, "%s", buffer);
214                                         }
215                                 } else {
216                                         printf("--------\n");
217                                 }
218                         }
219
220                         do {
221                                 fprintf(out, "%s", field);
222                                 if (!rapid && !sigint)
223                                         printf("%s", field);
224                         } while (state == BODY &&
225                                         (state = m_getfld(state, name,
226                                         field, sizeof(field), in)));
227
228                         if (prepend || !qbody)
229                                 break;
230
231                         printf("--------Enter additional text\n");
232 has_no_body:
233                         fflush(stdout);
234                         for (;;) {
235                                 getln(field, sizeof(field));
236                                 if (!*field)
237                                         break;
238                                 fprintf(out, "%s", field);
239                         }
240                         break;
241
242                 default:
243                         adios(EX_DATAERR, NULL, "skeleton is poorly formatted");
244                 }
245                 break;
246         }
247
248         if (qbody)
249                 printf("--------\n");
250
251         fflush(stdout);
252         fclose(in);
253         fclose(out);
254         SIGNAL(SIGINT, SIG_IGN);
255
256         if ((fdi = open(tmpfil, O_RDONLY)) == NOTOK) {
257                 adios(EX_IOERR, tmpfil, "unable to re-open");
258         }
259         if ((fdo = creat(drft, m_gmprot())) == NOTOK) {
260                 adios(EX_IOERR, drft, "unable to write");
261         }
262         cpydata(fdi, fdo, tmpfil, drft);
263         close(fdi);
264         close(fdo);
265         unlink(tmpfil);
266
267         context_save();  /* save the context file */
268         return EX_OK;
269 }
270
271
272 int
273 getln(char *buffer, int n)
274 {
275         int c;
276         sig_atomic_t psigint = sigint;
277         char *cp;
278
279         cp = buffer;
280         *cp = '\0';
281
282         switch (setjmp(sigenv)) {
283         case 0:
284                 wtuser = 1;
285                 break;
286
287         default:
288                 wtuser = 0;
289                 if (sigint == psigint) {
290                         return 0;
291                 } else {
292                         sigint = psigint;
293                         return NOTOK;
294                 }
295         }
296
297         for (;;) {
298                 switch (c = getchar()) {
299                 case EOF:
300                         clearerr(stdin);
301                         longjmp(sigenv, DONE);
302
303                 case '\n':
304                         if (cp[-1] == '\\') {
305                                 cp[-1] = c;
306                                 wtuser = 0;
307                                 return 1;
308                         }
309                         *cp++ = c;
310                         *cp = '\0';
311                         wtuser = 0;
312                         return 0;
313
314                 default:
315                         if (cp < buffer + n)
316                                 *cp++ = c;
317                         *cp = '\0';
318                 }
319         }
320 }
321
322
323 static void
324 intrser(int i)
325 {
326         if (wtuser) {
327                 close(STDIN_FILENO);
328         }
329         sigint++;
330 }