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