Fix missing va_end call in uip/mhmisc.c
[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 = {{0}};
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 LENERR2:
144                         state = FLD2;
145                         /* FALL */
146
147                 case FLD2:
148                         /*
149                         ** Check if the value of field contains
150                         ** anything other than space or tab.
151                         */
152                         for (cp = f.value; *cp; cp++) {
153                                 if (*cp != ' ' && *cp != '\t') {
154                                         break;
155                                 }
156                         }
157
158                         /* If so, just add header line to draft */
159                         if (*cp++ != '\n' || *cp) {
160                                 printf("%s:%s", f.name, f.value);
161                                 fprintf(out, "%s:%s", f.name, f.value);
162                         } else {
163                                 /* Else, get value of header field */
164                                 printf("%s: ", f.name);
165                                 fflush(stdout);
166                                 i = getln(buffer, sizeof(buffer));
167                                 if (i == -1) {
168 abort:
169                                         unlink(tmpfil);
170                                         exit(EX_DATAERR);
171                                 }
172                                 if (i || (buffer[0]!='\n' && buffer[0]!='\0')) {
173                                         fprintf(out, "%s:", f.name);
174                                         do {
175                                                 if (buffer[0] != ' ' && buffer[0] != '\t') {
176                                                         putc(' ', out);
177                                                 }
178                                                 fprintf(out, "%s", buffer);
179                                         } while (i == 1 && (i = getln(buffer, sizeof(buffer))) >= 0);
180                                         if (i == -1) {
181                                                 goto abort;
182                                         }
183                                 }
184                         }
185                         continue;
186
187                 case BODY2:
188                 case FILEEOF2:
189                         fprintf(out, "--------\n");
190                         if (qbody) {
191                                 if (f.value == NULL) {
192                                         printf("--------\n");
193                                         goto has_no_body;
194                                 }
195
196                                 if (prepend) {
197                                         printf("--------Enter initial text\n");
198                                         fflush(stdout);
199                                         for (;;) {
200                                                 getln(buffer, sizeof(buffer));
201                                                 if (!*buffer) {
202                                                         break;
203                                                 }
204                                                 fprintf(out, "%s", buffer);
205                                         }
206                                 } else {
207                                         printf("--------\n");
208                                 }
209                         }
210
211                         if (state == BODY2) {
212                                 do {
213                                         fprintf(out, "%s", f.value);
214                                         if (!rapid && !sigint) {
215                                                 printf("%s", f.value);
216                                         }
217                                 } while ((state = m_getfld2(state, &f, in))
218                                                 ==BODY2);
219                                 if (state != FILEEOF2) {
220                                         adios(EX_IOERR, "m_getfld2", "io error");
221                                 }
222                         }
223
224                         if (prepend || !qbody) {
225                                 break;
226                         }
227
228                         printf("--------Enter additional text\n");
229 has_no_body:
230                         fflush(stdout);
231                         for (;;) {
232                                 getln(buffer, sizeof(buffer));
233                                 if (!*buffer) {
234                                         break;
235                                 }
236                                 fprintf(out, "%s", buffer);
237                         }
238                         break;
239
240                 case FMTERR2:
241                         advise(NULL, "skeleton is poorly formatted");
242                         continue;
243                 default:
244                         adios(EX_IOERR, "m_getfld2", "io error");
245                 }
246                 break;
247         }
248
249         if (qbody) {
250                 printf("--------\n");
251         }
252
253         fflush(stdout);
254         fclose(in);
255         fclose(out);
256         SIGNAL(SIGINT, SIG_IGN);
257
258         if ((fdi = open(tmpfil, O_RDONLY)) == NOTOK) {
259                 adios(EX_IOERR, tmpfil, "unable to re-open");
260         }
261         if ((fdo = creat(drft, m_gmprot())) == NOTOK) {
262                 adios(EX_IOERR, drft, "unable to write");
263         }
264         cpydata(fdi, fdo, tmpfil, drft);
265         close(fdi);
266         close(fdo);
267         unlink(tmpfil);
268
269         context_save();
270         return EX_OK;
271 }
272
273
274 int
275 getln(char *buffer, int n)
276 {
277         int c;
278         sig_atomic_t psigint = sigint;
279         char *cp;
280
281         cp = buffer;
282         *cp = '\0';
283
284         switch (setjmp(sigenv)) {
285         case 0:
286                 wtuser = 1;
287                 break;
288
289         default:
290                 wtuser = 0;
291                 if (sigint == psigint) {
292                         return 0;
293                 } else {
294                         sigint = psigint;
295                         return NOTOK;
296                 }
297         }
298
299         for (;;) {
300                 switch (c = getchar()) {
301                 case EOF:
302                         clearerr(stdin);
303                         longjmp(sigenv, DONE);
304
305                 case '\n':
306                         if (cp[-1] == '\\') {
307                                 cp[-1] = c;
308                                 wtuser = 0;
309                                 return 1;
310                         }
311                         *cp++ = c;
312                         *cp = '\0';
313                         wtuser = 0;
314                         return 0;
315
316                 default:
317                         if (cp < buffer + n)
318                                 *cp++ = c;
319                         *cp = '\0';
320                 }
321         }
322 }
323
324
325 static void
326 intrser(int i)
327 {
328         if (wtuser) {
329                 close(STDIN_FILENO);
330         }
331         sigint++;
332 }