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