]> diplodocus.org Git - nmh/blob - uip/prompter.c
Use va_copy() to get a copy of va_list, instead of using original.
[nmh] / uip / prompter.c
1 /* prompter.c -- simple prompting editor front-end
2 *
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include <h/mh.h>
9 #include <fcntl.h>
10 #include <h/signals.h>
11 #include "h/done.h"
12 #include <h/utils.h>
13 #include "sbr/m_mktemp.h"
14 #include <setjmp.h>
15
16 #include <termios.h>
17
18 #define QUOTE '\\'
19
20 #define PROMPTER_SWITCHES \
21 X("erase chr", 0, ERASESW) \
22 X("kill chr", 0, KILLSW) \
23 X("prepend", 0, PREPSW) \
24 X("noprepend", 0, NPREPSW) \
25 X("rapid", 0, RAPDSW) \
26 X("norapid", 0, NRAPDSW) \
27 X("body", -4, BODYSW) \
28 X("nobody", -6, NBODYSW) \
29 X("doteof", 0, DOTSW) \
30 X("nodoteof", 0, NDOTSW) \
31 X("version", 0, VERSIONSW) \
32 X("help", 0, HELPSW) \
33
34 #define X(sw, minchars, id) id,
35 DEFINE_SWITCH_ENUM(PROMPTER);
36 #undef X
37
38 #define X(sw, minchars, id) { sw, minchars, id },
39 DEFINE_SWITCH_ARRAY(PROMPTER, switches);
40 #undef X
41
42 static struct termios tio;
43
44 static bool wtuser;
45 static bool sigint;
46 static jmp_buf sigenv;
47
48 /*
49 * prototypes
50 */
51 static int getln (char *, int);
52 static int chrcnv (char *);
53 static void chrdsp (char *, char);
54 static void intrser (int);
55
56
57 int
58 main (int argc, char **argv)
59 {
60 bool body = true;
61 bool prepend = true;
62 bool rapid = false;
63 bool doteof = false;
64 int fdi, fdo, i, state;
65 char *cp, *drft = NULL, *erasep = NULL;
66 char *killp = NULL, name[NAMESZ], field[NMH_BUFSIZ];
67 char buffer[BUFSIZ];
68 char **arguments, **argp;
69 FILE *in, *out;
70 char *tmpfil;
71 m_getfld_state_t gstate;
72
73 if (nmh_init(argv[0], true, false)) { return 1; }
74
75 arguments = getarguments (invo_name, argc, argv, 1);
76 argp = arguments;
77
78 while ((cp = *argp++))
79 if (*cp == '-') {
80 switch (smatch (++cp, switches)) {
81 case AMBIGSW:
82 ambigsw (cp, switches);
83 done (1);
84 case UNKWNSW:
85 die("-%s unknown", cp);
86
87 case HELPSW:
88 snprintf (buffer, sizeof(buffer), "%s [switches] file",
89 invo_name);
90 print_help (buffer, switches, 1);
91 done (0);
92 case VERSIONSW:
93 print_version(invo_name);
94 done (0);
95
96 case ERASESW:
97 if (!(erasep = *argp++) || *erasep == '-')
98 die("missing argument to %s", argp[-2]);
99 continue;
100 case KILLSW:
101 if (!(killp = *argp++) || *killp == '-')
102 die("missing argument to %s", argp[-2]);
103 continue;
104
105 case PREPSW:
106 prepend = true;
107 continue;
108 case NPREPSW:
109 prepend = false;
110 continue;
111
112 case RAPDSW:
113 rapid = true;
114 continue;
115 case NRAPDSW:
116 rapid = false;
117 continue;
118
119 case BODYSW:
120 body = true;
121 continue;
122 case NBODYSW:
123 body = false;
124 continue;
125
126 case DOTSW:
127 doteof = true;
128 continue;
129 case NDOTSW:
130 doteof = false;
131 continue;
132 }
133 } else {
134 if (!drft)
135 drft = cp;
136 }
137
138 if (!drft)
139 die("usage: %s [switches] file", invo_name);
140 if ((in = fopen (drft, "r")) == NULL)
141 adios (drft, "unable to open");
142
143 if ((tmpfil = m_mktemp2(NULL, invo_name, NULL, &out)) == NULL) {
144 die("unable to create temporary file in %s", get_temp_dir());
145 }
146
147 /*
148 * Are we changing the kill or erase character?
149 */
150 if (killp || erasep) {
151 cc_t save_erase, save_kill;
152
153 /* get the current terminal attributes */
154 tcgetattr(0, &tio);
155
156 /* save original kill, erase character for later */
157 save_kill = tio.c_cc[VKILL];
158 save_erase = tio.c_cc[VERASE];
159
160 /* set new kill, erase character in terminal structure */
161 tio.c_cc[VKILL] = killp ? chrcnv (killp) : save_kill;
162 tio.c_cc[VERASE] = erasep ? chrcnv (erasep) : save_erase;
163
164 /* set the new terminal attributes */
165 tcsetattr(0, TCSADRAIN, &tio);
166
167 /* print out new kill erase characters */
168 chrdsp ("erase", tio.c_cc[VERASE]);
169 chrdsp (", kill", tio.c_cc[VKILL]);
170 chrdsp (", intr", tio.c_cc[VINTR]);
171 putchar ('\n');
172 fflush (stdout);
173
174 /*
175 * We set the kill and erase character back to original
176 * setup in terminal structure so we can easily
177 * restore it upon exit.
178 */
179 tio.c_cc[VKILL] = save_kill;
180 tio.c_cc[VERASE] = save_erase;
181 }
182
183 sigint = false;
184 SIGNAL2 (SIGINT, intrser);
185
186 /*
187 * Loop through the lines of the draft skeleton.
188 */
189 gstate = m_getfld_state_init(in);
190 for (;;) {
191 int fieldsz = sizeof field;
192 switch (state = m_getfld2(&gstate, name, field, &fieldsz)) {
193 case FLD:
194 case FLDPLUS:
195 /*
196 * Check if the value of field contains anything
197 * other than space or tab.
198 */
199 for (cp = field; *cp; cp++)
200 if (*cp != ' ' && *cp != '\t')
201 break;
202
203 /* If so, just add header line to draft */
204 if (*cp++ != '\n' || *cp != 0) {
205 printf ("%s:%s", name, field);
206 fprintf (out, "%s:%s", name, field);
207 while (state == FLDPLUS) {
208 fieldsz = sizeof field;
209 state = m_getfld2(&gstate, name, field, &fieldsz);
210 fputs(field, stdout);
211 fputs(field, out);
212 }
213 } else {
214 /* Else, get value of header field */
215 printf ("%s: ", name);
216 fflush (stdout);
217 i = getln (field, sizeof(field));
218 if (i == -1) {
219 abort:
220 if (killp || erasep) {
221 tcsetattr(0, TCSADRAIN, &tio);
222 }
223 (void) m_unlink (tmpfil);
224 done (1);
225 }
226 if (i != 0 || (field[0] != '\n' && field[0] != 0)) {
227 fprintf (out, "%s:", name);
228 do {
229 if (field[0] != ' ' && field[0] != '\t')
230 putc (' ', out);
231 fputs(field, out);
232 } while (i == 1
233 && (i = getln (field, sizeof(field))) >= 0);
234 if (i == -1)
235 goto abort;
236 }
237 }
238
239 continue;
240
241 case BODY:
242 case FILEEOF:
243 if (!body)
244 break;
245 fprintf (out, "--------\n");
246 if (field[0] == 0 || !prepend)
247 puts("--------");
248 if (field[0]) {
249 if (prepend && body) {
250 puts("\n--------Enter initial text\n");
251 fflush (stdout);
252 for (;;) {
253 getln (buffer, sizeof(buffer));
254 if (doteof && buffer[0] == '.' && buffer[1] == '\n')
255 break;
256 if (buffer[0] == 0)
257 break;
258 fputs(buffer, out);
259 }
260 }
261
262 do {
263 fputs(field, out);
264 if (!rapid && !sigint)
265 fputs(field, stdout);
266 } while (state == BODY &&
267 (fieldsz = sizeof field,
268 state = m_getfld2(&gstate, name, field, &fieldsz)));
269 if (prepend || !body)
270 break;
271 puts("\n--------Enter additional text\n");
272 }
273
274 fflush (stdout);
275 for (;;) {
276 getln (field, sizeof(field));
277 if (doteof && field[0] == '.' && field[1] == '\n')
278 break;
279 if (field[0] == 0)
280 break;
281 fputs(field, out);
282 }
283 break;
284
285 default:
286 die("skeleton is poorly formatted");
287 }
288 break;
289 }
290 m_getfld_state_destroy (&gstate);
291
292 if (body)
293 puts("--------");
294
295 fflush (stdout);
296 fclose (in);
297 fclose (out);
298 SIGNAL (SIGINT, SIG_IGN);
299
300 if (killp || erasep) {
301 tcsetattr(0, TCSADRAIN, &tio);
302 }
303
304 if ((fdi = open (tmpfil, O_RDONLY)) == NOTOK)
305 adios (tmpfil, "unable to re-open");
306 if ((fdo = creat (drft, m_gmprot ())) == NOTOK)
307 adios (drft, "unable to write");
308 cpydata (fdi, fdo, tmpfil, drft);
309 close (fdi);
310 close (fdo);
311 (void) m_unlink (tmpfil);
312
313 context_save (); /* save the context file */
314 done (0);
315 return 1;
316 }
317
318
319 static int
320 getln (char *buffer, int n)
321 {
322 int c;
323 char *cp;
324 static bool quoting;
325
326 *buffer = 0;
327
328 switch (setjmp (sigenv)) {
329 case OK:
330 wtuser = true;
331 break;
332
333 case DONE:
334 wtuser = false;
335 return 0;
336
337 default:
338 wtuser = false;
339 return NOTOK;
340 }
341
342 cp = buffer;
343 *cp = 0;
344
345 for (;;) {
346 switch (c = getchar ()) {
347 case EOF:
348 quoting = false;
349 clearerr (stdin);
350 longjmp (sigenv, DONE);
351
352 case '\n':
353 if (quoting) {
354 *(cp - 1) = c;
355 quoting = false;
356 wtuser = false;
357 return 1;
358 }
359 *cp++ = c;
360 *cp = 0;
361 wtuser = false;
362 return 0;
363
364 default:
365 if (c == QUOTE) {
366 quoting = true;
367 } else {
368 quoting = false;
369 }
370 if (cp < buffer + n)
371 *cp++ = c;
372 *cp = 0;
373 }
374 }
375 }
376
377
378 static void
379 intrser (int i)
380 {
381 NMH_UNUSED (i);
382
383 if (wtuser)
384 longjmp (sigenv, NOTOK);
385 sigint = true;
386 }
387
388
389 static int
390 chrcnv (char *cp)
391 {
392 return *cp != QUOTE ? *cp : m_atoi(++cp);
393 }
394
395
396 static void
397 chrdsp (char *s, char c)
398 {
399 printf ("%s ", s);
400 if (c < ' ' || c == 0177)
401 printf ("^%c", c ^ 0100);
402 else
403 printf ("%c", c);
404 }