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