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