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