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