]> diplodocus.org Git - nmh/blob - uip/prompter.c
488210df48c6a45edbc6700c70183005cb7c3d95
[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 <signal.h>
14 #include <setjmp.h>
15
16 #include <termios.h>
17
18 #define QUOTE '\\'
19
20 #ifndef CKILL
21 # define CKILL '@'
22 #endif
23
24 #ifndef CERASE
25 # define CERASE '#'
26 #endif
27
28 #define PROMPTER_SWITCHES \
29 X("erase chr", 0, ERASESW) \
30 X("kill chr", 0, KILLSW) \
31 X("prepend", 0, PREPSW) \
32 X("noprepend", 0, NPREPSW) \
33 X("rapid", 0, RAPDSW) \
34 X("norapid", 0, NRAPDSW) \
35 X("body", -4, BODYSW) \
36 X("nobody", -6, NBODYSW) \
37 X("doteof", 0, DOTSW) \
38 X("nodoteof", 0, NDOTSW) \
39 X("version", 0, VERSIONSW) \
40 X("help", 0, HELPSW) \
41
42 #define X(sw, minchars, id) id,
43 DEFINE_SWITCH_ENUM(PROMPTER);
44 #undef X
45
46 #define X(sw, minchars, id) { sw, minchars, id },
47 DEFINE_SWITCH_ARRAY(PROMPTER, switches);
48 #undef X
49
50
51 static struct termios tio;
52 #define ERASE tio.c_cc[VERASE]
53 #define KILL tio.c_cc[VKILL]
54 #define INTR tio.c_cc[VINTR]
55
56 static int wtuser = 0;
57 static int sigint = 0;
58 static jmp_buf sigenv;
59
60 /*
61 * prototypes
62 */
63 int getln (char *, int);
64 static int chrcnv (char *);
65 static void chrdsp (char *, char);
66 static void intrser (int);
67
68
69 int
70 main (int argc, char **argv)
71 {
72 int body = 1, prepend = 1, rapid = 0;
73 int doteof = 0, fdi, fdo, i, state;
74 char *cp, *drft = NULL, *erasep = NULL;
75 char *killp = NULL, name[NAMESZ], field[BUFSIZ];
76 char buffer[BUFSIZ], tmpfil[BUFSIZ];
77 char **arguments, **argp;
78 FILE *in, *out;
79 char *tfile = NULL;
80 m_getfld_state_t gstate = 0;
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 (;;) {
206 int fieldsz = sizeof field;
207 switch (state = m_getfld (&gstate, name, field, &fieldsz, in)) {
208 case FLD:
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 fieldsz = sizeof field;
224 state = m_getfld (&gstate, name, field, &fieldsz, 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 continue;
255
256 case BODY:
257 case FILEEOF:
258 if (!body)
259 break;
260 fprintf (out, "--------\n");
261 if (field[0] == 0 || !prepend)
262 printf ("--------\n");
263 if (field[0]) {
264 if (prepend && body) {
265 printf ("\n--------Enter initial text\n\n");
266 fflush (stdout);
267 for (;;) {
268 getln (buffer, sizeof(buffer));
269 if (doteof && buffer[0] == '.' && buffer[1] == '\n')
270 break;
271 if (buffer[0] == 0)
272 break;
273 fprintf (out, "%s", buffer);
274 }
275 }
276
277 do {
278 fprintf (out, "%s", field);
279 if (!rapid && !sigint)
280 printf ("%s", field);
281 } while (state == BODY &&
282 (fieldsz = sizeof field,
283 state = m_getfld (&gstate, name, field, &fieldsz, in)));
284 if (prepend || !body)
285 break;
286 else
287 printf ("\n--------Enter additional text\n\n");
288 }
289
290 fflush (stdout);
291 for (;;) {
292 getln (field, sizeof(field));
293 if (doteof && field[0] == '.' && field[1] == '\n')
294 break;
295 if (field[0] == 0)
296 break;
297 fprintf (out, "%s", field);
298 }
299 break;
300
301 default:
302 adios (NULL, "skeleton is poorly formatted");
303 }
304 break;
305 }
306 m_getfld_state_destroy (&gstate);
307
308 if (body)
309 printf ("--------\n");
310
311 fflush (stdout);
312 fclose (in);
313 fclose (out);
314 SIGNAL (SIGINT, SIG_IGN);
315
316 if (killp || erasep) {
317 tcsetattr(0, TCSADRAIN, &tio);
318 }
319
320 if ((fdi = open (tmpfil, O_RDONLY)) == NOTOK)
321 adios (tmpfil, "unable to re-open");
322 if ((fdo = creat (drft, m_gmprot ())) == NOTOK)
323 adios (drft, "unable to write");
324 cpydata (fdi, fdo, tmpfil, drft);
325 close (fdi);
326 close (fdo);
327 unlink (tmpfil);
328
329 context_save (); /* save the context file */
330 done (0);
331 return 1;
332 }
333
334
335 int
336 getln (char *buffer, int n)
337 {
338 int c;
339 char *cp;
340
341 cp = buffer;
342 *cp = 0;
343
344 switch (setjmp (sigenv)) {
345 case OK:
346 wtuser = 1;
347 break;
348
349 case DONE:
350 wtuser = 0;
351 return 0;
352
353 default:
354 wtuser = 0;
355 return NOTOK;
356 }
357
358 for (;;) {
359 switch (c = getchar ()) {
360 case EOF:
361 clearerr (stdin);
362 longjmp (sigenv, DONE);
363
364 case '\n':
365 if (cp[-1] == QUOTE) {
366 cp[-1] = c;
367 wtuser = 0;
368 return 1;
369 }
370 *cp++ = c;
371 *cp = 0;
372 wtuser = 0;
373 return 0;
374
375 default:
376 if (cp < buffer + n)
377 *cp++ = c;
378 *cp = 0;
379 }
380 }
381 }
382
383
384 static void
385 intrser (int i)
386 {
387 NMH_UNUSED (i);
388
389 if (wtuser)
390 longjmp (sigenv, NOTOK);
391 sigint++;
392 }
393
394
395 static int
396 chrcnv (char *cp)
397 {
398 return (*cp != QUOTE ? *cp : m_atoi (++cp));
399 }
400
401
402 static void
403 chrdsp (char *s, char c)
404 {
405 printf ("%s ", s);
406 if (c < ' ' || c == 0177)
407 printf ("^%c", c ^ 0100);
408 else
409 printf ("%c", c);
410 }