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