]> diplodocus.org Git - nmh/blob - uip/prompter.c
7923e84b316508ccb9fc60fdc43f2212b0c3abfe
[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 setlocale(LC_ALL, "");
82 invo_name = r1bindex (argv[0], '/');
83
84 /* read user profile/context */
85 context_read();
86
87 arguments = getarguments (invo_name, argc, argv, 1);
88 argp = arguments;
89
90 while ((cp = *argp++))
91 if (*cp == '-') {
92 switch (smatch (++cp, switches)) {
93 case AMBIGSW:
94 ambigsw (cp, switches);
95 done (1);
96 case UNKWNSW:
97 adios (NULL, "-%s unknown", cp);
98
99 case HELPSW:
100 snprintf (buffer, sizeof(buffer), "%s [switches] file",
101 invo_name);
102 print_help (buffer, switches, 1);
103 done (0);
104 case VERSIONSW:
105 print_version(invo_name);
106 done (0);
107
108 case ERASESW:
109 if (!(erasep = *argp++) || *erasep == '-')
110 adios (NULL, "missing argument to %s", argp[-2]);
111 continue;
112 case KILLSW:
113 if (!(killp = *argp++) || *killp == '-')
114 adios (NULL, "missing argument to %s", argp[-2]);
115 continue;
116
117 case PREPSW:
118 prepend++;
119 continue;
120 case NPREPSW:
121 prepend = 0;
122 continue;
123
124 case RAPDSW:
125 rapid++;
126 continue;
127 case NRAPDSW:
128 rapid = 0;
129 continue;
130
131 case BODYSW:
132 body++;
133 continue;
134 case NBODYSW:
135 body = 0;
136 continue;
137
138 case DOTSW:
139 doteof++;
140 continue;
141 case NDOTSW:
142 doteof = 0;
143 continue;
144 }
145 } else {
146 if (!drft)
147 drft = cp;
148 }
149
150 if (!drft)
151 adios (NULL, "usage: %s [switches] file", invo_name);
152 if ((in = fopen (drft, "r")) == NULL)
153 adios (drft, "unable to open");
154
155 tmpfil = m_mktemp2(NULL, invo_name, NULL, &out);
156 if (tmpfil == NULL) adios("prompter", "unable to create temporary file");
157 chmod (tmpfil, 0600);
158
159 /*
160 * Are we changing the kill or erase character?
161 */
162 if (killp || erasep) {
163 cc_t save_erase, save_kill;
164
165 /* get the current terminal attributes */
166 tcgetattr(0, &tio);
167
168 /* save original kill, erase character for later */
169 save_kill = KILL;
170 save_erase = ERASE;
171
172 /* set new kill, erase character in terminal structure */
173 KILL = killp ? chrcnv (killp) : save_kill;
174 ERASE = erasep ? chrcnv (erasep) : save_erase;
175
176 /* set the new terminal attributes */
177 tcsetattr(0, TCSADRAIN, &tio);
178
179 /* print out new kill erase characters */
180 chrdsp ("erase", ERASE);
181 chrdsp (", kill", KILL);
182 chrdsp (", intr", INTR);
183 putchar ('\n');
184 fflush (stdout);
185
186 /*
187 * We set the kill and erase character back to original
188 * setup in terminal structure so we can easily
189 * restore it upon exit.
190 */
191 KILL = save_kill;
192 ERASE = save_erase;
193 }
194
195 sigint = 0;
196 SIGNAL2 (SIGINT, intrser);
197
198 /*
199 * Loop through the lines of the draft skeleton.
200 */
201 for (;;) {
202 int fieldsz = sizeof field;
203 switch (state = m_getfld (&gstate, name, field, &fieldsz, in)) {
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_getfld (&gstate, name, field, &fieldsz, in);
221 printf ("%s", field);
222 fprintf (out, "%s", field);
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 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 fprintf (out, "%s", field);
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 printf ("--------\n");
259 if (field[0]) {
260 if (prepend && body) {
261 printf ("\n--------Enter initial text\n\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 fprintf (out, "%s", buffer);
270 }
271 }
272
273 do {
274 fprintf (out, "%s", field);
275 if (!rapid && !sigint)
276 printf ("%s", field);
277 } while (state == BODY &&
278 (fieldsz = sizeof field,
279 state = m_getfld (&gstate, name, field, &fieldsz, in)));
280 if (prepend || !body)
281 break;
282 else
283 printf ("\n--------Enter additional text\n\n");
284 }
285
286 fflush (stdout);
287 for (;;) {
288 getln (field, sizeof(field));
289 if (doteof && field[0] == '.' && field[1] == '\n')
290 break;
291 if (field[0] == 0)
292 break;
293 fprintf (out, "%s", field);
294 }
295 break;
296
297 default:
298 adios (NULL, "skeleton is poorly formatted");
299 }
300 break;
301 }
302 m_getfld_state_destroy (&gstate);
303
304 if (body)
305 printf ("--------\n");
306
307 fflush (stdout);
308 fclose (in);
309 fclose (out);
310 SIGNAL (SIGINT, SIG_IGN);
311
312 if (killp || erasep) {
313 tcsetattr(0, TCSADRAIN, &tio);
314 }
315
316 if ((fdi = open (tmpfil, O_RDONLY)) == NOTOK)
317 adios (tmpfil, "unable to re-open");
318 if ((fdo = creat (drft, m_gmprot ())) == NOTOK)
319 adios (drft, "unable to write");
320 cpydata (fdi, fdo, tmpfil, drft);
321 close (fdi);
322 close (fdo);
323 unlink (tmpfil);
324
325 context_save (); /* save the context file */
326 done (0);
327 return 1;
328 }
329
330
331 int
332 getln (char *buffer, int n)
333 {
334 int c;
335 char *cp;
336
337 cp = buffer;
338 *cp = 0;
339
340 switch (setjmp (sigenv)) {
341 case OK:
342 wtuser = 1;
343 break;
344
345 case DONE:
346 wtuser = 0;
347 return 0;
348
349 default:
350 wtuser = 0;
351 return NOTOK;
352 }
353
354 for (;;) {
355 switch (c = getchar ()) {
356 case EOF:
357 clearerr (stdin);
358 longjmp (sigenv, DONE);
359
360 case '\n':
361 if (cp[-1] == QUOTE) {
362 cp[-1] = c;
363 wtuser = 0;
364 return 1;
365 }
366 *cp++ = c;
367 *cp = 0;
368 wtuser = 0;
369 return 0;
370
371 default:
372 if (cp < buffer + n)
373 *cp++ = c;
374 *cp = 0;
375 }
376 }
377 }
378
379
380 static void
381 intrser (int i)
382 {
383 NMH_UNUSED (i);
384
385 if (wtuser)
386 longjmp (sigenv, NOTOK);
387 sigint++;
388 }
389
390
391 static int
392 chrcnv (char *cp)
393 {
394 return (*cp != QUOTE ? *cp : m_atoi (++cp));
395 }
396
397
398 static void
399 chrdsp (char *s, char c)
400 {
401 printf ("%s ", s);
402 if (c < ' ' || c == 0177)
403 printf ("^%c", c ^ 0100);
404 else
405 printf ("%c", c);
406 }