1 | |
---|
2 | /* |
---|
3 | * prompter.c -- simple prompting editor front-end |
---|
4 | * |
---|
5 | * $Id: prompter.c,v 1.1.1.1 1999-02-07 18:14:16 danw Exp $ |
---|
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", 4 }, |
---|
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 | 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 | } |
---|