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