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