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