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