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