Rearranged whitespace (and comments) in all the code!
[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), "%s [switches] file",
132                                                 invo_name);
133                                         print_help (buffer, switches, 1);
134                                         done (1);
135                                 case VERSIONSW:
136                                         print_version(invo_name);
137                                         done (1);
138
139                                 case ERASESW:
140                                         if (!(erasep = *argp++) || *erasep == '-')
141                                                 adios (NULL, "missing argument to %s", argp[-2]);
142                                         continue;
143                                 case KILLSW:
144                                         if (!(killp = *argp++) || *killp == '-')
145                                                 adios (NULL, "missing argument to %s", argp[-2]);
146                                         continue;
147
148                                 case PREPSW:
149                                         prepend++;
150                                         continue;
151                                 case NPREPSW:
152                                         prepend = 0;
153                                         continue;
154
155                                 case RAPDSW:
156                                         rapid++;
157                                         continue;
158                                 case NRAPDSW:
159                                         rapid = 0;
160                                         continue;
161
162                                 case BODYSW:
163                                         body++;
164                                         continue;
165                                 case NBODYSW:
166                                         body = 0;
167                                         continue;
168
169                                 case DOTSW:
170                                         doteof++;
171                                         continue;
172                                 case NDOTSW:
173                                         doteof = 0;
174                                         continue;
175                         }
176                 } else {
177                         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) adios("prompter", "unable to create temporary file");
188         chmod (tmpfil, 0600);
189         strncpy (tmpfil, tfile, sizeof(tmpfil));
190
191         /*
192          * Are we changing the kill or erase character?
193          */
194         if (killp || erasep) {
195 #ifdef HAVE_TERMIOS_H
196                 cc_t save_erase, save_kill;
197 #else
198                 int save_erase, save_kill;
199 #endif
200
201                 /* get the current terminal attributes */
202 #ifdef HAVE_TERMIOS_H
203                 tcgetattr(0, &tio);
204 #else
205 # ifdef HAVE_TERMIO_H
206                 ioctl(0, TCGETA, &tio);
207 # else
208                 ioctl (0, TIOCGETP, (char *) &tio);
209                 ioctl (0, TIOCGETC, (char *) &tc);
210 # endif
211 #endif
212
213                 /* save original kill, erase character for later */
214                 save_kill = KILL;
215                 save_erase = ERASE;
216
217                 /* set new kill, erase character in terminal structure */
218                 KILL = killp ? chrcnv (killp) : save_kill;
219                 ERASE = erasep ? chrcnv (erasep) : save_erase;
220
221                 /* set the new terminal attributes */
222 #ifdef HAVE_TERMIOS_H
223                  tcsetattr(0, TCSADRAIN, &tio);
224 #else
225 # ifdef HAVE_TERMIO_H
226                 ioctl(0, TCSETAW, &tio);
227 # else
228                 ioctl (0, TIOCSETN, (char *) &tio);
229 # endif
230 #endif
231
232                 /* print out new kill erase characters */
233                 chrdsp ("erase", ERASE);
234                 chrdsp (", kill", KILL);
235                 chrdsp (", intr", INTR);
236                 putchar ('\n');
237                 fflush (stdout);
238
239                 /*
240                  * We set the kill and erase character back to original
241                  * setup in terminal structure so we can easily
242                  * restore it upon exit.
243                  */
244                 KILL = save_kill;
245                 ERASE = save_erase;
246         }
247
248         sigint = 0;
249         SIGNAL2 (SIGINT, intrser);
250
251         /*
252          * Loop through the lines of the draft skeleton.
253          */
254         for (state = FLD;;) {
255                 switch (state = m_getfld (state, name, field, sizeof(field), in)) {
256                         case FLD:
257                         case FLDEOF:
258                         case FLDPLUS:
259                                 /*
260                                  * Check if the value of field contains anything
261                                  * other than space or tab.
262                                  */
263                                 for (cp = field; *cp; cp++)
264                                         if (*cp != ' ' && *cp != '\t')
265                                                 break;
266
267                                 /* If so, just add header line to draft */
268                                 if (*cp++ != '\n' || *cp != 0) {
269                                         printf ("%s:%s", name, field);
270                                         fprintf (out, "%s:%s", name, field);
271                                         while (state == FLDPLUS) {
272                                                 state =
273                                                         m_getfld (state, name, field, sizeof(field), in);
274                                                 printf ("%s", field);
275                                                 fprintf (out, "%s", field);
276                                         }
277                                 } else {
278                                         /* Else, get value of header field */
279                                         printf ("%s: ", name);
280                                         fflush (stdout);
281                                         i = getln (field, sizeof(field));
282                                         if (i == -1) {
283 abort:
284                                                 if (killp || erasep) {
285 #ifdef HAVE_TERMIOS_H
286                                                         tcsetattr(0, TCSADRAIN, &tio);
287 #else
288 # ifdef HAVE_TERMIO
289                                                         ioctl (0, TCSETA, &tio);
290 # else
291                                                         ioctl (0, TIOCSETN, (char *) &tio);
292 # endif
293 #endif
294                                                 }
295                                                 unlink (tmpfil);
296                                                 done (1);
297                                         }
298                                         if (i != 0 || (field[0] != '\n' && field[0] != 0)) {
299                                                 fprintf (out, "%s:", name);
300                                                 do {
301                                                         if (field[0] != ' ' && field[0] != '\t')
302                                                                 putc (' ', out);
303                                                         fprintf (out, "%s", field);
304                                                 } while (i == 1
305                                                                         && (i = getln (field, sizeof(field))) >= 0);
306                                                 if (i == -1)
307                                                         goto abort;
308                                         }
309                                 }
310
311                                 if (state == FLDEOF) {  /* moby hack */
312                                         fprintf (out, "--------\n");
313                                         printf ("--------\n");
314                                         if (!body)
315                                                 break;
316                                         goto no_body;
317                                 }
318                                 continue;
319
320                         case BODY:
321                         case BODYEOF:
322                         case FILEEOF:
323                                 if (!body)
324                                         break;
325                                 fprintf (out, "--------\n");
326                                 if (field[0] == 0 || !prepend)
327                                         printf ("--------\n");
328                                 if (field[0]) {
329                                         if (prepend && body) {
330                                                 printf ("\n--------Enter initial text\n\n");
331                                                 fflush (stdout);
332                                                 for (;;) {
333                                                         getln (buffer, sizeof(buffer));
334                                                         if (doteof && buffer[0] == '.' && buffer[1] == '\n')
335                                                                 break;
336                                                         if (buffer[0] == 0)
337                                                                 break;
338                                                         fprintf (out, "%s", buffer);
339                                                 }
340                                         }
341
342                                         do {
343                                                 fprintf (out, "%s", field);
344                                                 if (!rapid && !sigint)
345                                                         printf ("%s", field);
346                                         } while (state == BODY &&
347                                                         (state = m_getfld (state, name, field, sizeof(field), in)));
348                                         if (prepend || !body)
349                                                 break;
350                                         else
351                                                 printf ("\n--------Enter additional text\n\n");
352                                 }
353 no_body:
354                                 fflush (stdout);
355                                 for (;;) {
356                                         getln (field, sizeof(field));
357                                         if (doteof && field[0] == '.' && field[1] == '\n')
358                                                 break;
359                                         if (field[0] == 0)
360                                                 break;
361                                          fprintf (out, "%s", field);
362                                 }
363                                 break;
364
365                         default:
366                                 adios (NULL, "skeleton is poorly formatted");
367                 }
368                 break;
369         }
370
371         if (body)
372                 printf ("--------\n");
373
374         fflush (stdout);
375         fclose (in);
376         fclose (out);
377         SIGNAL (SIGINT, SIG_IGN);
378
379         if (killp || erasep) {
380 #ifdef HAVE_TERMIOS_H
381                  tcsetattr(0, TCSADRAIN, &tio);
382 #else
383 # ifdef HAVE_TERMIO_H
384                 ioctl (0, TCSETAW, &tio);
385 # else
386                 ioctl (0, TIOCSETN, (char *) &tio);
387 # endif
388 #endif
389         }
390
391         if ((fdi = open (tmpfil, O_RDONLY)) == NOTOK)
392                 adios (tmpfil, "unable to re-open");
393         if ((fdo = creat (drft, m_gmprot ())) == NOTOK)
394                 adios (drft, "unable to write");
395         cpydata (fdi, fdo, tmpfil, drft);
396         close (fdi);
397         close (fdo);
398         unlink (tmpfil);
399
400         context_save ();  /* save the context file */
401         done (0);
402         return 1;
403 }
404
405
406 int
407 getln (char *buffer, int n)
408 {
409         int c;
410         char *cp;
411
412         cp = buffer;
413         *cp = 0;
414
415         switch (setjmp (sigenv)) {
416                 case OK:
417                         wtuser = 1;
418                         break;
419
420                 case DONE:
421                         wtuser = 0;
422                         return 0;
423
424                 default:
425                         wtuser = 0;
426                         return NOTOK;
427         }
428
429         for (;;) {
430                 switch (c = getchar ()) {
431                         case EOF:
432                                 clearerr (stdin);
433                                 longjmp (sigenv, DONE);
434
435                         case '\n':
436                                 if (cp[-1] == QUOTE) {
437                                         cp[-1] = c;
438                                         wtuser = 0;
439                                         return 1;
440                                 }
441                                 *cp++ = c;
442                                 *cp = 0;
443                                 wtuser = 0;
444                                 return 0;
445
446                         default:
447                                 if (cp < buffer + n)
448                                         *cp++ = c;
449                                 *cp = 0;
450                 }
451         }
452 }
453
454
455 static RETSIGTYPE
456 intrser (int i)
457 {
458 #ifndef RELIABLE_SIGNALS
459         SIGNAL (SIGINT, intrser);
460 #endif
461
462         if (wtuser)
463                 longjmp (sigenv, NOTOK);
464         sigint++;
465 }
466
467
468 static int
469 chrcnv (char *cp)
470 {
471         return (*cp != QUOTE ? *cp : m_atoi (++cp));
472 }
473
474
475 static void
476 chrdsp (char *s, char c)
477 {
478         printf ("%s ", s);
479         if (c < ' ' || c == 0177)
480                 printf ("^%c", c ^ 0100);
481         else
482                 printf ("%c", c);
483 }