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