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