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