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