Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / usr.bin / ul / ul.c
1 /*
2  * Copyright (c) 1980, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1980, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)ul.c     8.1 (Berkeley) 6/6/93
35  * $FreeBSD: src/usr.bin/ul/ul.c,v 1.6.2.1 2000/08/23 08:49:49 kris Exp $
36  * $DragonFly: src/usr.bin/ul/ul.c,v 1.2 2003/06/17 04:29:33 dillon Exp $
37  */
38
39 #include <err.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <termcap.h>
44 #include <unistd.h>
45
46 #define IESC    '\033'
47 #define SO      '\016'
48 #define SI      '\017'
49 #define HFWD    '9'
50 #define HREV    '8'
51 #define FREV    '7'
52 #define MAXBUF  512
53
54 #define NORMAL  000
55 #define ALTSET  001     /* Reverse */
56 #define SUPERSC 002     /* Dim */
57 #define SUBSC   004     /* Dim | Ul */
58 #define UNDERL  010     /* Ul */
59 #define BOLD    020     /* Bold */
60
61 int     must_use_uc, must_overstrike;
62 char    *CURS_UP, *CURS_RIGHT, *CURS_LEFT,
63         *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
64         *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
65
66 struct  CHAR    {
67         char    c_mode;
68         char    c_char;
69 } ;
70
71 struct  CHAR    obuf[MAXBUF];
72 int     col, maxcol;
73 int     mode;
74 int     halfpos;
75 int     upln;
76 int     iflag;
77
78 static void usage __P((void));
79 void setnewmode __P((int));
80 void initcap __P((void));
81 void reverse __P((void));
82 int outchar __P((int));
83 void fwd __P((void));
84 void initbuf __P((void));
85 void iattr __P((void));
86 void overstrike __P((void));
87 void flushln __P((void));
88 void filter __P((FILE *));
89 void outc __P((int));
90
91 #define PRINT(s)        if (s == NULL) /* void */; else tputs(s, 1, outchar)
92
93 int
94 main(argc, argv)
95         int argc;
96         char **argv;
97 {
98         int c;
99         char *termtype;
100         FILE *f;
101         char termcap[1024];
102
103         termtype = getenv("TERM");
104         if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
105                 termtype = "lpr";
106         while ((c=getopt(argc, argv, "it:T:")) != -1)
107                 switch(c) {
108
109                 case 't':
110                 case 'T': /* for nroff compatibility */
111                                 termtype = optarg;
112                         break;
113                 case 'i':
114                         iflag = 1;
115                         break;
116                 default:
117                         usage();
118                 }
119
120         switch(tgetent(termcap, termtype)) {
121
122         case 1:
123                 break;
124
125         default:
126                 warnx("trouble reading termcap");
127                 /* fall through to ... */
128
129         case 0:
130                 /* No such terminal type - assume dumb */
131                 (void)strcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:");
132                 break;
133         }
134         initcap();
135         if (    (tgetflag("os") && ENTER_BOLD==NULL ) ||
136                 (tgetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL))
137                         must_overstrike = 1;
138         initbuf();
139         if (optind == argc)
140                 filter(stdin);
141         else for (; optind<argc; optind++) {
142                 f = fopen(argv[optind],"r");
143                 if (f == NULL)
144                         err(1, "%s", argv[optind]);
145                 else
146                         filter(f);
147         }
148         exit(0);
149 }
150
151 static void
152 usage()
153 {
154         fprintf(stderr, "usage: ul [-i] [-t terminal] file...\n");
155         exit(1);
156 }
157
158 void
159 filter(f)
160         FILE *f;
161 {
162         register c;
163
164         while ((c = getc(f)) != EOF && col < MAXBUF) switch(c) {
165
166         case '\b':
167                 if (col > 0)
168                         col--;
169                 continue;
170
171         case '\t':
172                 col = (col+8) & ~07;
173                 if (col > maxcol)
174                         maxcol = col;
175                 continue;
176
177         case '\r':
178                 col = 0;
179                 continue;
180
181         case SO:
182                 mode |= ALTSET;
183                 continue;
184
185         case SI:
186                 mode &= ~ALTSET;
187                 continue;
188
189         case IESC:
190                 switch (c = getc(f)) {
191
192                 case HREV:
193                         if (halfpos == 0) {
194                                 mode |= SUPERSC;
195                                 halfpos--;
196                         } else if (halfpos > 0) {
197                                 mode &= ~SUBSC;
198                                 halfpos--;
199                         } else {
200                                 halfpos = 0;
201                                 reverse();
202                         }
203                         continue;
204
205                 case HFWD:
206                         if (halfpos == 0) {
207                                 mode |= SUBSC;
208                                 halfpos++;
209                         } else if (halfpos < 0) {
210                                 mode &= ~SUPERSC;
211                                 halfpos++;
212                         } else {
213                                 halfpos = 0;
214                                 fwd();
215                         }
216                         continue;
217
218                 case FREV:
219                         reverse();
220                         continue;
221
222                 default:
223                         errx(1, "unknown escape sequence in input: %o, %o", IESC, c);
224                 }
225                 continue;
226
227         case '_':
228                 if (obuf[col].c_char)
229                         obuf[col].c_mode |= UNDERL | mode;
230                 else
231                         obuf[col].c_char = '_';
232         case ' ':
233                 col++;
234                 if (col > maxcol)
235                         maxcol = col;
236                 continue;
237
238         case '\n':
239                 flushln();
240                 continue;
241
242         case '\f':
243                 flushln();
244                 putchar('\f');
245                 continue;
246
247         default:
248                 if (c < ' ')    /* non printing */
249                         continue;
250                 if (obuf[col].c_char == '\0') {
251                         obuf[col].c_char = c;
252                         obuf[col].c_mode = mode;
253                 } else if (obuf[col].c_char == '_') {
254                         obuf[col].c_char = c;
255                         obuf[col].c_mode |= UNDERL|mode;
256                 } else if (obuf[col].c_char == c)
257                         obuf[col].c_mode |= BOLD|mode;
258                 else
259                         obuf[col].c_mode = mode;
260                 col++;
261                 if (col > maxcol)
262                         maxcol = col;
263                 continue;
264         }
265         if (maxcol)
266                 flushln();
267 }
268
269 void
270 flushln()
271 {
272         register lastmode;
273         register i;
274         int hadmodes = 0;
275
276         lastmode = NORMAL;
277         for (i=0; i<maxcol; i++) {
278                 if (obuf[i].c_mode != lastmode) {
279                         hadmodes++;
280                         setnewmode(obuf[i].c_mode);
281                         lastmode = obuf[i].c_mode;
282                 }
283                 if (obuf[i].c_char == '\0') {
284                         if (upln)
285                                 PRINT(CURS_RIGHT);
286                         else
287                                 outc(' ');
288                 } else
289                         outc(obuf[i].c_char);
290         }
291         if (lastmode != NORMAL) {
292                 setnewmode(0);
293         }
294         if (must_overstrike && hadmodes)
295                 overstrike();
296         putchar('\n');
297         if (iflag && hadmodes)
298                 iattr();
299         (void)fflush(stdout);
300         if (upln)
301                 upln--;
302         initbuf();
303 }
304
305 /*
306  * For terminals that can overstrike, overstrike underlines and bolds.
307  * We don't do anything with halfline ups and downs, or Greek.
308  */
309 void
310 overstrike()
311 {
312         register int i;
313         char lbuf[256];
314         register char *cp = lbuf;
315         int hadbold=0;
316
317         /* Set up overstrike buffer */
318         for (i=0; i<maxcol; i++)
319                 switch (obuf[i].c_mode) {
320                 case NORMAL:
321                 default:
322                         *cp++ = ' ';
323                         break;
324                 case UNDERL:
325                         *cp++ = '_';
326                         break;
327                 case BOLD:
328                         *cp++ = obuf[i].c_char;
329                         hadbold=1;
330                         break;
331                 }
332         putchar('\r');
333         for (*cp=' '; *cp==' '; cp--)
334                 *cp = 0;
335         for (cp=lbuf; *cp; cp++)
336                 putchar(*cp);
337         if (hadbold) {
338                 putchar('\r');
339                 for (cp=lbuf; *cp; cp++)
340                         putchar(*cp=='_' ? ' ' : *cp);
341                 putchar('\r');
342                 for (cp=lbuf; *cp; cp++)
343                         putchar(*cp=='_' ? ' ' : *cp);
344         }
345 }
346
347 void
348 iattr()
349 {
350         register int i;
351         char lbuf[256];
352         register char *cp = lbuf;
353
354         for (i=0; i<maxcol; i++)
355                 switch (obuf[i].c_mode) {
356                 case NORMAL:    *cp++ = ' '; break;
357                 case ALTSET:    *cp++ = 'g'; break;
358                 case SUPERSC:   *cp++ = '^'; break;
359                 case SUBSC:     *cp++ = 'v'; break;
360                 case UNDERL:    *cp++ = '_'; break;
361                 case BOLD:      *cp++ = '!'; break;
362                 default:        *cp++ = 'X'; break;
363                 }
364         for (*cp=' '; *cp==' '; cp--)
365                 *cp = 0;
366         for (cp=lbuf; *cp; cp++)
367                 putchar(*cp);
368         putchar('\n');
369 }
370
371 void
372 initbuf()
373 {
374
375         bzero((char *)obuf, sizeof (obuf));     /* depends on NORMAL == 0 */
376         col = 0;
377         maxcol = 0;
378         mode &= ALTSET;
379 }
380
381 void
382 fwd()
383 {
384         register oldcol, oldmax;
385
386         oldcol = col;
387         oldmax = maxcol;
388         flushln();
389         col = oldcol;
390         maxcol = oldmax;
391 }
392
393 void
394 reverse()
395 {
396         upln++;
397         fwd();
398         PRINT(CURS_UP);
399         PRINT(CURS_UP);
400         upln++;
401 }
402
403 void
404 initcap()
405 {
406         static char tcapbuf[512];
407         char *bp = tcapbuf;
408
409         /* This nonsense attempts to work with both old and new termcap */
410         CURS_UP =               tgetstr("up", &bp);
411         CURS_RIGHT =            tgetstr("ri", &bp);
412         if (CURS_RIGHT == NULL)
413                 CURS_RIGHT =    tgetstr("nd", &bp);
414         CURS_LEFT =             tgetstr("le", &bp);
415         if (CURS_LEFT == NULL)
416                 CURS_LEFT =     tgetstr("bc", &bp);
417         if (CURS_LEFT == NULL && tgetflag("bs"))
418                 CURS_LEFT =     "\b";
419
420         ENTER_STANDOUT =        tgetstr("so", &bp);
421         EXIT_STANDOUT =         tgetstr("se", &bp);
422         ENTER_UNDERLINE =       tgetstr("us", &bp);
423         EXIT_UNDERLINE =        tgetstr("ue", &bp);
424         ENTER_DIM =             tgetstr("mh", &bp);
425         ENTER_BOLD =            tgetstr("md", &bp);
426         ENTER_REVERSE =         tgetstr("mr", &bp);
427         EXIT_ATTRIBUTES =       tgetstr("me", &bp);
428
429         if (!ENTER_BOLD && ENTER_REVERSE)
430                 ENTER_BOLD = ENTER_REVERSE;
431         if (!ENTER_BOLD && ENTER_STANDOUT)
432                 ENTER_BOLD = ENTER_STANDOUT;
433         if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
434                 ENTER_UNDERLINE = ENTER_STANDOUT;
435                 EXIT_UNDERLINE = EXIT_STANDOUT;
436         }
437         if (!ENTER_DIM && ENTER_STANDOUT)
438                 ENTER_DIM = ENTER_STANDOUT;
439         if (!ENTER_REVERSE && ENTER_STANDOUT)
440                 ENTER_REVERSE = ENTER_STANDOUT;
441         if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
442                 EXIT_ATTRIBUTES = EXIT_STANDOUT;
443
444         /*
445          * Note that we use REVERSE for the alternate character set,
446          * not the as/ae capabilities.  This is because we are modelling
447          * the model 37 teletype (since that's what nroff outputs) and
448          * the typical as/ae is more of a graphics set, not the greek
449          * letters the 37 has.
450          */
451
452         UNDER_CHAR =            tgetstr("uc", &bp);
453         must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
454 }
455
456 int
457 outchar(c)
458         int c;
459 {
460         return(putchar(c & 0177));
461 }
462
463 static int curmode = 0;
464
465 void
466 outc(c)
467         int c;
468 {
469         putchar(c);
470         if (must_use_uc && (curmode&UNDERL)) {
471                 PRINT(CURS_LEFT);
472                 PRINT(UNDER_CHAR);
473         }
474 }
475
476 void
477 setnewmode(newmode)
478         int newmode;
479 {
480         if (!iflag) {
481                 if (curmode != NORMAL && newmode != NORMAL)
482                         setnewmode(NORMAL);
483                 switch (newmode) {
484                 case NORMAL:
485                         switch(curmode) {
486                         case NORMAL:
487                                 break;
488                         case UNDERL:
489                                 PRINT(EXIT_UNDERLINE);
490                                 break;
491                         default:
492                                 /* This includes standout */
493                                 PRINT(EXIT_ATTRIBUTES);
494                                 break;
495                         }
496                         break;
497                 case ALTSET:
498                         PRINT(ENTER_REVERSE);
499                         break;
500                 case SUPERSC:
501                         /*
502                          * This only works on a few terminals.
503                          * It should be fixed.
504                          */
505                         PRINT(ENTER_UNDERLINE);
506                         PRINT(ENTER_DIM);
507                         break;
508                 case SUBSC:
509                         PRINT(ENTER_DIM);
510                         break;
511                 case UNDERL:
512                         PRINT(ENTER_UNDERLINE);
513                         break;
514                 case BOLD:
515                         PRINT(ENTER_BOLD);
516                         break;
517                 default:
518                         /*
519                          * We should have some provision here for multiple modes
520                          * on at once.  This will have to come later.
521                          */
522                         PRINT(ENTER_STANDOUT);
523                         break;
524                 }
525         }
526         curmode = newmode;
527 }