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