]> diplodocus.org Git - nmh/blob - uip/prompter.c
6f97a8806a2a01a6fa2860285d02457add14375d
[nmh] / uip / prompter.c
1
2 /*
3 * prompter.c -- simple prompting editor front-end
4 *
5 * $Id$
6 *
7 * This code is Copyright (c) 2002, by the authors of nmh. See the
8 * COPYRIGHT file in the root directory of the nmh distribution for
9 * complete copyright information.
10 */
11
12 #include <h/mh.h>
13 #include <fcntl.h>
14 #include <h/signals.h>
15 #include <errno.h>
16 #include <signal.h>
17 #include <setjmp.h>
18
19 #ifdef HAVE_TERMIOS_H
20 # include <termios.h>
21 #else
22 # ifdef HAVE_TERMIO_H
23 # include <termio.h>
24 # else
25 # include <sgtty.h>
26 # endif
27 #endif
28
29 #define QUOTE '\\'
30
31 #ifndef CKILL
32 # define CKILL '@'
33 #endif
34
35 #ifndef CERASE
36 # define CERASE '#'
37 #endif
38
39 static struct swit switches[] = {
40 #define ERASESW 0
41 { "erase chr", 0 },
42 #define KILLSW 1
43 { "kill chr", 0 },
44 #define PREPSW 2
45 { "prepend", 0 },
46 #define NPREPSW 3
47 { "noprepend", 0 },
48 #define RAPDSW 4
49 { "rapid", 0 },
50 #define NRAPDSW 5
51 { "norapid", 0 },
52 #define BODYSW 6
53 { "body", -4 },
54 #define NBODYSW 7
55 { "nobody", -6 },
56 #define DOTSW 8
57 { "doteof", 0 },
58 #define NDOTSW 9
59 { "nodoteof", 0 },
60 #define VERSIONSW 10
61 { "version", 0 },
62 #define HELPSW 11
63 { "help", 0 },
64 { NULL, 0 }
65 };
66
67
68 #ifdef HAVE_TERMIOS_H
69 static struct termios tio;
70 # define ERASE tio.c_cc[VERASE]
71 # define KILL tio.c_cc[VKILL]
72 # define INTR tio.c_cc[VINTR]
73 #else
74 # ifdef HAVE_TERMIO_H
75 static struct termio tio;
76 # define ERASE tio.c_cc[VERASE]
77 # define KILL tio.c_cc[VKILL]
78 # define INTR tio.c_cc[VINTR]
79 # else
80 static struct sgttyb tio;
81 static struct tchars tc;
82 # define ERASE tio.sg_erase
83 # define KILL tio.sg_kill
84 # define INTR tc.t_intrc
85 # endif
86 #endif
87
88 static int wtuser = 0;
89 static int sigint = 0;
90 static jmp_buf sigenv;
91
92 /*
93 * prototypes
94 */
95 int getln (char *, int);
96 static int chrcnv (char *);
97 static void chrdsp (char *, char);
98 static RETSIGTYPE intrser (int);
99
100
101 int
102 main (int argc, char **argv)
103 {
104 int body = 1, prepend = 1, rapid = 0;
105 int doteof = 0, fdi, fdo, i, state;
106 char *cp, *drft = NULL, *erasep = NULL;
107 char *killp = NULL, name[NAMESZ], field[BUFSIZ];
108 char buffer[BUFSIZ], tmpfil[BUFSIZ];
109 char **arguments, **argp;
110 FILE *in, *out;
111 char *tfile = NULL;
112
113 #ifdef LOCALE
114 setlocale(LC_ALL, "");
115 #endif
116 invo_name = r1bindex (argv[0], '/');
117
118 /* read user profile/context */
119 context_read();
120
121 arguments = getarguments (invo_name, argc, argv, 1);
122 argp = arguments;
123
124 while ((cp = *argp++))
125 if (*cp == '-') {
126 switch (smatch (++cp, switches)) {
127 case AMBIGSW:
128 ambigsw (cp, switches);
129 done (1);
130 case UNKWNSW:
131 adios (NULL, "-%s unknown", cp);
132
133 case HELPSW:
134 snprintf (buffer, sizeof(buffer), "%s [switches] file",
135 invo_name);
136 print_help (buffer, switches, 1);
137 done (1);
138 case VERSIONSW:
139 print_version(invo_name);
140 done (1);
141
142 case ERASESW:
143 if (!(erasep = *argp++) || *erasep == '-')
144 adios (NULL, "missing argument to %s", argp[-2]);
145 continue;
146 case KILLSW:
147 if (!(killp = *argp++) || *killp == '-')
148 adios (NULL, "missing argument to %s", argp[-2]);
149 continue;
150
151 case PREPSW:
152 prepend++;
153 continue;
154 case NPREPSW:
155 prepend = 0;
156 continue;
157
158 case RAPDSW:
159 rapid++;
160 continue;
161 case NRAPDSW:
162 rapid = 0;
163 continue;
164
165 case BODYSW:
166 body++;
167 continue;
168 case NBODYSW:
169 body = 0;
170 continue;
171
172 case DOTSW:
173 doteof++;
174 continue;
175 case NDOTSW:
176 doteof = 0;
177 continue;
178 }
179 } else {
180 if (!drft)
181 drft = cp;
182 }
183
184 if (!drft)
185 adios (NULL, "usage: %s [switches] file", invo_name);
186 if ((in = fopen (drft, "r")) == NULL)
187 adios (drft, "unable to open");
188
189 tfile = m_mktemp2(NULL, invo_name, NULL, &out);
190 if (tfile == NULL) adios("prompter", "unable to create temporary file");
191 chmod (tmpfil, 0600);
192 strncpy (tmpfil, tfile, sizeof(tmpfil));
193
194 /*
195 * Are we changing the kill or erase character?
196 */
197 if (killp || erasep) {
198 #ifdef HAVE_TERMIOS_H
199 cc_t save_erase, save_kill;
200 #else
201 int save_erase, save_kill;
202 #endif
203
204 /* get the current terminal attributes */
205 #ifdef HAVE_TERMIOS_H
206 tcgetattr(0, &tio);
207 #else
208 # ifdef HAVE_TERMIO_H
209 ioctl(0, TCGETA, &tio);
210 # else
211 ioctl (0, TIOCGETP, (char *) &tio);
212 ioctl (0, TIOCGETC, (char *) &tc);
213 # endif
214 #endif
215
216 /* save original kill, erase character for later */
217 save_kill = KILL;
218 save_erase = ERASE;
219
220 /* set new kill, erase character in terminal structure */
221 KILL = killp ? chrcnv (killp) : save_kill;
222 ERASE = erasep ? chrcnv (erasep) : save_erase;
223
224 /* set the new terminal attributes */
225 #ifdef HAVE_TERMIOS_H
226 tcsetattr(0, TCSADRAIN, &tio);
227 #else
228 # ifdef HAVE_TERMIO_H
229 ioctl(0, TCSETAW, &tio);
230 # else
231 ioctl (0, TIOCSETN, (char *) &tio);
232 # endif
233 #endif
234
235 /* print out new kill erase characters */
236 chrdsp ("erase", ERASE);
237 chrdsp (", kill", KILL);
238 chrdsp (", intr", INTR);
239 putchar ('\n');
240 fflush (stdout);
241
242 /*
243 * We set the kill and erase character back to original
244 * setup in terminal structure so we can easily
245 * restore it upon exit.
246 */
247 KILL = save_kill;
248 ERASE = save_erase;
249 }
250
251 sigint = 0;
252 SIGNAL2 (SIGINT, intrser);
253
254 /*
255 * Loop through the lines of the draft skeleton.
256 */
257 for (state = FLD;;) {
258 switch (state = m_getfld (state, name, field, sizeof(field), in)) {
259 case FLD:
260 case FLDEOF:
261 case FLDPLUS:
262 /*
263 * Check if the value of field contains anything
264 * other than space or tab.
265 */
266 for (cp = field; *cp; cp++)
267 if (*cp != ' ' && *cp != '\t')
268 break;
269
270 /* If so, just add header line to draft */
271 if (*cp++ != '\n' || *cp != 0) {
272 printf ("%s:%s", name, field);
273 fprintf (out, "%s:%s", name, field);
274 while (state == FLDPLUS) {
275 state =
276 m_getfld (state, name, field, sizeof(field), in);
277 printf ("%s", field);
278 fprintf (out, "%s", field);
279 }
280 } else {
281 /* Else, get value of header field */
282 printf ("%s: ", name);
283 fflush (stdout);
284 i = getln (field, sizeof(field));
285 if (i == -1) {
286 abort:
287 if (killp || erasep) {
288 #ifdef HAVE_TERMIOS_H
289 tcsetattr(0, TCSADRAIN, &tio);
290 #else
291 # ifdef HAVE_TERMIO
292 ioctl (0, TCSETA, &tio);
293 # else
294 ioctl (0, TIOCSETN, (char *) &tio);
295 # endif
296 #endif
297 }
298 unlink (tmpfil);
299 done (1);
300 }
301 if (i != 0 || (field[0] != '\n' && field[0] != 0)) {
302 fprintf (out, "%s:", name);
303 do {
304 if (field[0] != ' ' && field[0] != '\t')
305 putc (' ', out);
306 fprintf (out, "%s", field);
307 } while (i == 1
308 && (i = getln (field, sizeof(field))) >= 0);
309 if (i == -1)
310 goto abort;
311 }
312 }
313
314 if (state == FLDEOF) { /* moby hack */
315 fprintf (out, "--------\n");
316 printf ("--------\n");
317 if (!body)
318 break;
319 goto no_body;
320 }
321 continue;
322
323 case BODY:
324 case BODYEOF:
325 case FILEEOF:
326 if (!body)
327 break;
328 fprintf (out, "--------\n");
329 if (field[0] == 0 || !prepend)
330 printf ("--------\n");
331 if (field[0]) {
332 if (prepend && body) {
333 printf ("\n--------Enter initial text\n\n");
334 fflush (stdout);
335 for (;;) {
336 getln (buffer, sizeof(buffer));
337 if (doteof && buffer[0] == '.' && buffer[1] == '\n')
338 break;
339 if (buffer[0] == 0)
340 break;
341 fprintf (out, "%s", buffer);
342 }
343 }
344
345 do {
346 fprintf (out, "%s", field);
347 if (!rapid && !sigint)
348 printf ("%s", field);
349 } while (state == BODY &&
350 (state = m_getfld (state, name, field, sizeof(field), in)));
351 if (prepend || !body)
352 break;
353 else
354 printf ("\n--------Enter additional text\n\n");
355 }
356 no_body:
357 fflush (stdout);
358 for (;;) {
359 getln (field, sizeof(field));
360 if (doteof && field[0] == '.' && field[1] == '\n')
361 break;
362 if (field[0] == 0)
363 break;
364 fprintf (out, "%s", field);
365 }
366 break;
367
368 default:
369 adios (NULL, "skeleton is poorly formatted");
370 }
371 break;
372 }
373
374 if (body)
375 printf ("--------\n");
376
377 fflush (stdout);
378 fclose (in);
379 fclose (out);
380 SIGNAL (SIGINT, SIG_IGN);
381
382 if (killp || erasep) {
383 #ifdef HAVE_TERMIOS_H
384 tcsetattr(0, TCSADRAIN, &tio);
385 #else
386 # ifdef HAVE_TERMIO_H
387 ioctl (0, TCSETAW, &tio);
388 # else
389 ioctl (0, TIOCSETN, (char *) &tio);
390 # endif
391 #endif
392 }
393
394 if ((fdi = open (tmpfil, O_RDONLY)) == NOTOK)
395 adios (tmpfil, "unable to re-open");
396 if ((fdo = creat (drft, m_gmprot ())) == NOTOK)
397 adios (drft, "unable to write");
398 cpydata (fdi, fdo, tmpfil, drft);
399 close (fdi);
400 close (fdo);
401 unlink (tmpfil);
402
403 context_save (); /* save the context file */
404 done (0);
405 return 1;
406 }
407
408
409 int
410 getln (char *buffer, int n)
411 {
412 int c;
413 char *cp;
414
415 cp = buffer;
416 *cp = 0;
417
418 switch (setjmp (sigenv)) {
419 case OK:
420 wtuser = 1;
421 break;
422
423 case DONE:
424 wtuser = 0;
425 return 0;
426
427 default:
428 wtuser = 0;
429 return NOTOK;
430 }
431
432 for (;;) {
433 switch (c = getchar ()) {
434 case EOF:
435 clearerr (stdin);
436 longjmp (sigenv, DONE);
437
438 case '\n':
439 if (cp[-1] == QUOTE) {
440 cp[-1] = c;
441 wtuser = 0;
442 return 1;
443 }
444 *cp++ = c;
445 *cp = 0;
446 wtuser = 0;
447 return 0;
448
449 default:
450 if (cp < buffer + n)
451 *cp++ = c;
452 *cp = 0;
453 }
454 }
455 }
456
457
458 static RETSIGTYPE
459 intrser (int i)
460 {
461 #ifndef RELIABLE_SIGNALS
462 SIGNAL (SIGINT, intrser);
463 #endif
464
465 if (wtuser)
466 longjmp (sigenv, NOTOK);
467 sigint++;
468 }
469
470
471 static int
472 chrcnv (char *cp)
473 {
474 return (*cp != QUOTE ? *cp : m_atoi (++cp));
475 }
476
477
478 static void
479 chrdsp (char *s, char c)
480 {
481 printf ("%s ", s);
482 if (c < ' ' || c == 0177)
483 printf ("^%c", c ^ 0100);
484 else
485 printf ("%c", c);
486 }