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