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