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