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