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