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