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