]> diplodocus.org Git - nmh/blob - uip/prompter.c
We're not using the .Bu macro anymore.
[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
112 #ifdef LOCALE
113 setlocale(LC_ALL, "");
114 #endif
115 invo_name = r1bindex (argv[0], '/');
116
117 /* read user profile/context */
118 context_read();
119
120 arguments = getarguments (invo_name, argc, argv, 1);
121 argp = arguments;
122
123 while ((cp = *argp++))
124 if (*cp == '-') {
125 switch (smatch (++cp, switches)) {
126 case AMBIGSW:
127 ambigsw (cp, switches);
128 done (1);
129 case UNKWNSW:
130 adios (NULL, "-%s unknown", cp);
131
132 case HELPSW:
133 snprintf (buffer, sizeof(buffer), "%s [switches] file",
134 invo_name);
135 print_help (buffer, switches, 1);
136 done (1);
137 case VERSIONSW:
138 print_version(invo_name);
139 done (1);
140
141 case ERASESW:
142 if (!(erasep = *argp++) || *erasep == '-')
143 adios (NULL, "missing argument to %s", argp[-2]);
144 continue;
145 case KILLSW:
146 if (!(killp = *argp++) || *killp == '-')
147 adios (NULL, "missing argument to %s", argp[-2]);
148 continue;
149
150 case PREPSW:
151 prepend++;
152 continue;
153 case NPREPSW:
154 prepend = 0;
155 continue;
156
157 case RAPDSW:
158 rapid++;
159 continue;
160 case NRAPDSW:
161 rapid = 0;
162 continue;
163
164 case BODYSW:
165 body++;
166 continue;
167 case NBODYSW:
168 body = 0;
169 continue;
170
171 case DOTSW:
172 doteof++;
173 continue;
174 case NDOTSW:
175 doteof = 0;
176 continue;
177 }
178 } else {
179 if (!drft)
180 drft = cp;
181 }
182
183 if (!drft)
184 adios (NULL, "usage: %s [switches] file", invo_name);
185 if ((in = fopen (drft, "r")) == NULL)
186 adios (drft, "unable to open");
187
188 strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
189 if ((out = fopen (tmpfil, "w")) == NULL)
190 adios (tmpfil, "unable to create");
191 chmod (tmpfil, 0600);
192
193 /*
194 * Are we changing the kill or erase character?
195 */
196 if (killp || erasep) {
197 #ifdef HAVE_TERMIOS_H
198 cc_t save_erase, save_kill;
199 #else
200 int save_erase, save_kill;
201 #endif
202
203 /* get the current terminal attributes */
204 #ifdef HAVE_TERMIOS_H
205 tcgetattr(0, &tio);
206 #else
207 # ifdef HAVE_TERMIO_H
208 ioctl(0, TCGETA, &tio);
209 # else
210 ioctl (0, TIOCGETP, (char *) &tio);
211 ioctl (0, TIOCGETC, (char *) &tc);
212 # endif
213 #endif
214
215 /* save original kill, erase character for later */
216 save_kill = KILL;
217 save_erase = ERASE;
218
219 /* set new kill, erase character in terminal structure */
220 KILL = killp ? chrcnv (killp) : save_kill;
221 ERASE = erasep ? chrcnv (erasep) : save_erase;
222
223 /* set the new terminal attributes */
224 #ifdef HAVE_TERMIOS_H
225 tcsetattr(0, TCSADRAIN, &tio);
226 #else
227 # ifdef HAVE_TERMIO_H
228 ioctl(0, TCSETAW, &tio);
229 # else
230 ioctl (0, TIOCSETN, (char *) &tio);
231 # endif
232 #endif
233
234 /* print out new kill erase characters */
235 chrdsp ("erase", ERASE);
236 chrdsp (", kill", KILL);
237 chrdsp (", intr", INTR);
238 putchar ('\n');
239 fflush (stdout);
240
241 /*
242 * We set the kill and erase character back to original
243 * setup in terminal structure so we can easily
244 * restore it upon exit.
245 */
246 KILL = save_kill;
247 ERASE = save_erase;
248 }
249
250 sigint = 0;
251 SIGNAL2 (SIGINT, intrser);
252
253 /*
254 * Loop through the lines of the draft skeleton.
255 */
256 for (state = FLD;;) {
257 switch (state = m_getfld (state, name, field, sizeof(field), in)) {
258 case FLD:
259 case FLDEOF:
260 case FLDPLUS:
261 /*
262 * Check if the value of field contains anything
263 * other than space or tab.
264 */
265 for (cp = field; *cp; cp++)
266 if (*cp != ' ' && *cp != '\t')
267 break;
268
269 /* If so, just add header line to draft */
270 if (*cp++ != '\n' || *cp != 0) {
271 printf ("%s:%s", name, field);
272 fprintf (out, "%s:%s", name, field);
273 while (state == FLDPLUS) {
274 state =
275 m_getfld (state, name, field, sizeof(field), in);
276 printf ("%s", field);
277 fprintf (out, "%s", field);
278 }
279 } else {
280 /* Else, get value of header field */
281 printf ("%s: ", name);
282 fflush (stdout);
283 i = getln (field, sizeof(field));
284 if (i == -1) {
285 abort:
286 if (killp || erasep) {
287 #ifdef HAVE_TERMIOS_H
288 tcsetattr(0, TCSADRAIN, &tio);
289 #else
290 # ifdef HAVE_TERMIO
291 ioctl (0, TCSETA, &tio);
292 # else
293 ioctl (0, TIOCSETN, (char *) &tio);
294 # endif
295 #endif
296 }
297 unlink (tmpfil);
298 done (1);
299 }
300 if (i != 0 || (field[0] != '\n' && field[0] != 0)) {
301 fprintf (out, "%s:", name);
302 do {
303 if (field[0] != ' ' && field[0] != '\t')
304 putc (' ', out);
305 fprintf (out, "%s", field);
306 } while (i == 1
307 && (i = getln (field, sizeof(field))) >= 0);
308 if (i == -1)
309 goto abort;
310 }
311 }
312
313 if (state == FLDEOF) { /* moby hack */
314 fprintf (out, "--------\n");
315 printf ("--------\n");
316 if (!body)
317 break;
318 goto no_body;
319 }
320 continue;
321
322 case BODY:
323 case BODYEOF:
324 case FILEEOF:
325 if (!body)
326 break;
327 fprintf (out, "--------\n");
328 if (field[0] == 0 || !prepend)
329 printf ("--------\n");
330 if (field[0]) {
331 if (prepend && body) {
332 printf ("\n--------Enter initial text\n\n");
333 fflush (stdout);
334 for (;;) {
335 getln (buffer, sizeof(buffer));
336 if (doteof && buffer[0] == '.' && buffer[1] == '\n')
337 break;
338 if (buffer[0] == 0)
339 break;
340 fprintf (out, "%s", buffer);
341 }
342 }
343
344 do {
345 fprintf (out, "%s", field);
346 if (!rapid && !sigint)
347 printf ("%s", field);
348 } while (state == BODY &&
349 (state = m_getfld (state, name, field, sizeof(field), in)));
350 if (prepend || !body)
351 break;
352 else
353 printf ("\n--------Enter additional text\n\n");
354 }
355 no_body:
356 fflush (stdout);
357 for (;;) {
358 getln (field, sizeof(field));
359 if (doteof && field[0] == '.' && field[1] == '\n')
360 break;
361 if (field[0] == 0)
362 break;
363 fprintf (out, "%s", field);
364 }
365 break;
366
367 default:
368 adios (NULL, "skeleton is poorly formatted");
369 }
370 break;
371 }
372
373 if (body)
374 printf ("--------\n");
375
376 fflush (stdout);
377 fclose (in);
378 fclose (out);
379 SIGNAL (SIGINT, SIG_IGN);
380
381 if (killp || erasep) {
382 #ifdef HAVE_TERMIOS_H
383 tcsetattr(0, TCSADRAIN, &tio);
384 #else
385 # ifdef HAVE_TERMIO_H
386 ioctl (0, TCSETAW, &tio);
387 # else
388 ioctl (0, TIOCSETN, (char *) &tio);
389 # endif
390 #endif
391 }
392
393 if ((fdi = open (tmpfil, O_RDONLY)) == NOTOK)
394 adios (tmpfil, "unable to re-open");
395 if ((fdo = creat (drft, m_gmprot ())) == NOTOK)
396 adios (drft, "unable to write");
397 cpydata (fdi, fdo, tmpfil, drft);
398 close (fdi);
399 close (fdo);
400 unlink (tmpfil);
401
402 context_save (); /* save the context file */
403 return done (0);
404 }
405
406
407 int
408 getln (char *buffer, int n)
409 {
410 int c;
411 char *cp;
412
413 cp = buffer;
414 *cp = 0;
415
416 switch (setjmp (sigenv)) {
417 case OK:
418 wtuser = 1;
419 break;
420
421 case DONE:
422 wtuser = 0;
423 return 0;
424
425 default:
426 wtuser = 0;
427 return NOTOK;
428 }
429
430 for (;;) {
431 switch (c = getchar ()) {
432 case EOF:
433 clearerr (stdin);
434 longjmp (sigenv, DONE);
435
436 case '\n':
437 if (cp[-1] == QUOTE) {
438 cp[-1] = c;
439 wtuser = 0;
440 return 1;
441 }
442 *cp++ = c;
443 *cp = 0;
444 wtuser = 0;
445 return 0;
446
447 default:
448 if (cp < buffer + n)
449 *cp++ = c;
450 *cp = 0;
451 }
452 }
453 }
454
455
456 static RETSIGTYPE
457 intrser (int i)
458 {
459 #ifndef RELIABLE_SIGNALS
460 SIGNAL (SIGINT, intrser);
461 #endif
462
463 if (wtuser)
464 longjmp (sigenv, NOTOK);
465 sigint++;
466 }
467
468
469 static int
470 chrcnv (char *cp)
471 {
472 return (*cp != QUOTE ? *cp : m_atoi (++cp));
473 }
474
475
476 static void
477 chrdsp (char *s, char c)
478 {
479 printf ("%s ", s);
480 if (c < ' ' || c == 0177)
481 printf ("^%c", c ^ 0100);
482 else
483 printf ("%c", c);
484 }