tizen 2.0
[framework/base/util-linux-ng.git] / text-utils / 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 /*
35  * modified by Kars de Jong <jongk@cs.utwente.nl>
36  *      to use terminfo instead of termcap.
37  * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
38  *      added Native Language Support
39  * 1999-09-19 Bruno Haible <haible@clisp.cons.org>
40  *      modified to work correctly in multi-byte locales
41  */
42
43 #include <stdio.h>
44 #include <unistd.h>             /* for getopt(), isatty() */
45 #include <string.h>             /* for memset(), strcpy() */
46 #include <term.h>               /* for setupterm() */
47 #include <stdlib.h>             /* for getenv() */
48 #include <limits.h>             /* for INT_MAX */
49 #include "nls.h"
50
51 #include "widechar.h"
52
53 #ifdef HAVE_WIDECHAR
54 static int put1wc(int c) /* Output an ASCII character as a wide character */
55 {
56   if (putwchar(c) == WEOF)
57     return EOF;
58   else
59     return c;
60 }
61 #define putwp(s) tputs(s,1,put1wc)
62 #else
63 #define putwp(s) putp(s)
64 #endif
65
66 void filter(FILE *f);
67 void flushln(void);
68 void overstrike(void);
69 void iattr(void);
70 void initbuf(void);
71 void fwd(void);
72 void reverse(void);
73 void initinfo(void);
74 void outc(wint_t c, int width);
75 void setmode(int newmode);
76 static void setcol(int newcol);
77 static void needcol(int col);
78
79 #define IESC    '\033'
80 #define SO      '\016'
81 #define SI      '\017'
82 #define HFWD    '9'
83 #define HREV    '8'
84 #define FREV    '7'
85
86 #define NORMAL  000
87 #define ALTSET  001     /* Reverse */
88 #define SUPERSC 002     /* Dim */
89 #define SUBSC   004     /* Dim | Ul */
90 #define UNDERL  010     /* Ul */
91 #define BOLD    020     /* Bold */
92 #define INITBUF 512
93
94 int     must_use_uc, must_overstrike;
95 char    *CURS_UP, *CURS_RIGHT, *CURS_LEFT,
96         *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
97         *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
98
99 struct  CHAR    {
100         char    c_mode;
101         wchar_t c_char;
102         int     c_width;
103 } ;
104
105 struct  CHAR    *obuf;
106 int     obuflen;                /* Tracks number of elements in obuf. */
107 int     col, maxcol;
108 int     mode;
109 int     halfpos;
110 int     upln;
111 int     iflag;
112
113 #define PRINT(s)        if (s == NULL) /* void */; else putwp(s)
114
115 int main(int argc, char **argv)
116 {
117         int c, ret;
118         char *termtype;
119         FILE *f;
120
121         setlocale(LC_ALL, "");
122         bindtextdomain(PACKAGE, LOCALEDIR);
123         textdomain(PACKAGE);
124
125         termtype = getenv("TERM");
126         if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
127                 termtype = "lpr";
128         while ((c = getopt(argc, argv, "it:T:")) != -1)
129                 switch(c) {
130
131                 case 't':
132                 case 'T': /* for nroff compatibility */
133                                 termtype = optarg;
134                         break;
135                 case 'i':
136                         iflag = 1;
137                         break;
138
139                 default:
140                         fprintf(stderr,
141                                 _("usage: %s [ -i ] [ -tTerm ] file...\n"),
142                                 argv[0]);
143                         exit(1);
144                 }
145         setupterm(termtype, 1, &ret);
146         switch(ret) {
147
148         case 1:
149                 break;
150
151         default:
152                 fprintf(stderr,_("trouble reading terminfo"));
153                 /* fall through to ... */
154
155         case 0:
156                 /* No such terminal type - assume dumb */
157                 setupterm("dumb", 1, (int *)0);
158                 break;
159         }
160         initinfo();
161         if (    (tigetflag("os") && ENTER_BOLD==NULL ) ||
162                 (tigetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL))
163                         must_overstrike = 1;
164         initbuf();
165         if (optind == argc)
166                 filter(stdin);
167         else for (; optind<argc; optind++) {
168                 f = fopen(argv[optind],"r");
169                 if (f == NULL) {
170                         perror(argv[optind]);
171                         exit(1);
172                 } else
173                         filter(f);
174         }
175         if (ferror(stdout) || fclose(stdout))
176                 return 1;
177         return 0;
178 }
179
180 void filter(FILE *f)
181 {
182         wint_t c;
183         int i, w;
184
185         while ((c = getwc(f)) != WEOF) switch(c) {
186
187         case '\b':
188                 setcol(col - 1);
189                 continue;
190
191         case '\t':
192                 setcol((col+8) & ~07);
193                 continue;
194
195         case '\r':
196                 setcol(0);
197                 continue;
198
199         case SO:
200                 mode |= ALTSET;
201                 continue;
202
203         case SI:
204                 mode &= ~ALTSET;
205                 continue;
206
207         case IESC:
208                 switch (c = getwc(f)) {
209
210                 case HREV:
211                         if (halfpos == 0) {
212                                 mode |= SUPERSC;
213                                 halfpos--;
214                         } else if (halfpos > 0) {
215                                 mode &= ~SUBSC;
216                                 halfpos--;
217                         } else {
218                                 halfpos = 0;
219                                 reverse();
220                         }
221                         continue;
222
223                 case HFWD:
224                         if (halfpos == 0) {
225                                 mode |= SUBSC;
226                                 halfpos++;
227                         } else if (halfpos < 0) {
228                                 mode &= ~SUPERSC;
229                                 halfpos++;
230                         } else {
231                                 halfpos = 0;
232                                 fwd();
233                         }
234                         continue;
235
236                 case FREV:
237                         reverse();
238                         continue;
239
240                 default:
241                         fprintf(stderr,
242                                 _("Unknown escape sequence in input: %o, %o\n"),
243                                 IESC, c);
244                         exit(1);
245                 }
246                 continue;
247
248         case '_':
249                 if (obuf[col].c_char || obuf[col].c_width < 0) {
250                         while(col > 0 && obuf[col].c_width < 0)
251                                 col--;
252                         w = obuf[col].c_width;
253                         for (i = 0; i < w; i++)
254                                 obuf[col++].c_mode |= UNDERL | mode;
255                         setcol(col);
256                         continue;
257                 }
258                 obuf[col].c_char = '_';
259                 obuf[col].c_width = 1;
260                 /* fall through */
261         case ' ':
262                 setcol(col + 1);
263                 continue;
264
265         case '\n':
266                 flushln();
267                 continue;
268
269         case '\f':
270                 flushln();
271                 putwchar('\f');
272                 continue;
273
274         default:
275                 if (!iswprint(c))       /* non printing */
276                         continue;
277                 w = wcwidth(c);
278                 needcol(col + w);
279                 if (obuf[col].c_char == '\0') {
280                         obuf[col].c_char = c;
281                         for (i = 0; i < w; i++)
282                                 obuf[col+i].c_mode = mode;
283                         obuf[col].c_width = w;
284                         for (i = 1; i < w; i++)
285                                 obuf[col+i].c_width = -1;
286                 } else if (obuf[col].c_char == '_') {
287                         obuf[col].c_char = c;
288                         for (i = 0; i < w; i++)
289                                 obuf[col+i].c_mode |= UNDERL|mode;
290                         obuf[col].c_width = w;
291                         for (i = 1; i < w; i++)
292                                 obuf[col+i].c_width = -1;
293                 } else if (obuf[col].c_char == c) {
294                         for (i = 0; i < w; i++)
295                                 obuf[col+i].c_mode |= BOLD|mode;
296                 } else {
297                         w = obuf[col].c_width;
298                         for (i = 0; i < w; i++)
299                                 obuf[col+i].c_mode = mode;
300                 }
301                 setcol(col + w);
302                 continue;
303         }
304         if (maxcol)
305                 flushln();
306 }
307
308 void flushln(void)
309 {
310         int lastmode;
311         int i;
312         int hadmodes = 0;
313
314         lastmode = NORMAL;
315         for (i=0; i<maxcol; i++) {
316                 if (obuf[i].c_mode != lastmode) {
317                         hadmodes++;
318                         setmode(obuf[i].c_mode);
319                         lastmode = obuf[i].c_mode;
320                 }
321                 if (obuf[i].c_char == '\0') {
322                         if (upln) {
323                                 PRINT(CURS_RIGHT);
324                         } else
325                                 outc(' ', 1);
326                 } else
327                         outc(obuf[i].c_char, obuf[i].c_width);
328                 if (obuf[i].c_width > 1)
329                         i += obuf[i].c_width -1;
330         }
331         if (lastmode != NORMAL) {
332                 setmode(0);
333         }
334         if (must_overstrike && hadmodes)
335                 overstrike();
336         putwchar('\n');
337         if (iflag && hadmodes)
338                 iattr();
339         (void)fflush(stdout);
340         if (upln)
341                 upln--;
342         initbuf();
343 }
344
345 /*
346  * For terminals that can overstrike, overstrike underlines and bolds.
347  * We don't do anything with halfline ups and downs, or Greek.
348  */
349 void overstrike(void)
350 {
351         register int i;
352 #ifdef __GNUC__
353         register wchar_t *lbuf = __builtin_alloca((maxcol+1)*sizeof(wchar_t));
354 #else
355         wchar_t lbuf[256];
356 #endif
357         register wchar_t *cp = lbuf;
358         int hadbold=0;
359
360         /* Set up overstrike buffer */
361         for (i=0; i<maxcol; i++)
362                 switch (obuf[i].c_mode) {
363                 case NORMAL:
364                 default:
365                         *cp++ = ' ';
366                         break;
367                 case UNDERL:
368                         *cp++ = '_';
369                         break;
370                 case BOLD:
371                         *cp++ = obuf[i].c_char;
372                         if (obuf[i].c_width > 1)
373                                 i += obuf[i].c_width - 1;
374                         hadbold=1;
375                         break;
376                 }
377         putwchar('\r');
378         for (*cp=' '; *cp==' '; cp--)
379                 *cp = 0;
380         for (cp=lbuf; *cp; cp++)
381                 putwchar(*cp);
382         if (hadbold) {
383                 putwchar('\r');
384                 for (cp=lbuf; *cp; cp++)
385                         putwchar(*cp=='_' ? ' ' : *cp);
386                 putwchar('\r');
387                 for (cp=lbuf; *cp; cp++)
388                         putwchar(*cp=='_' ? ' ' : *cp);
389         }
390 }
391
392 void iattr(void)
393 {
394         register int i;
395 #ifdef __GNUC__
396         register char *lbuf = __builtin_alloca((maxcol+1)*sizeof(char));
397 #else
398         char lbuf[256];
399 #endif
400         register char *cp = lbuf;
401
402         for (i=0; i<maxcol; i++)
403                 switch (obuf[i].c_mode) {
404                 case NORMAL:    *cp++ = ' '; break;
405                 case ALTSET:    *cp++ = 'g'; break;
406                 case SUPERSC:   *cp++ = '^'; break;
407                 case SUBSC:     *cp++ = 'v'; break;
408                 case UNDERL:    *cp++ = '_'; break;
409                 case BOLD:      *cp++ = '!'; break;
410                 default:        *cp++ = 'X'; break;
411                 }
412         for (*cp=' '; *cp==' '; cp--)
413                 *cp = 0;
414         for (cp=lbuf; *cp; cp++)
415                 putwchar(*cp);
416         putwchar('\n');
417 }
418
419 void initbuf(void)
420 {
421         if (obuf == NULL) {     /* First time. */
422                 obuflen = INITBUF;
423                 obuf = malloc(sizeof(struct CHAR) * obuflen);
424                 if (obuf == NULL) {
425                         fprintf(stderr, _("Unable to allocate buffer.\n"));
426                         exit(1);
427                 }
428         }
429
430         /* assumes NORMAL == 0 */
431         memset(obuf, 0, sizeof(struct CHAR) * obuflen);
432         setcol(0);
433         maxcol = 0;
434         mode &= ALTSET;
435 }
436
437 void fwd(void)
438 {
439         int oldcol, oldmax;
440
441         oldcol = col;
442         oldmax = maxcol;
443         flushln();
444         setcol(oldcol);
445         maxcol = oldmax;
446 }
447
448 void reverse(void)
449 {
450         upln++;
451         fwd();
452         PRINT(CURS_UP);
453         PRINT(CURS_UP);
454         upln++;
455 }
456
457 void initinfo(void)
458 {
459         CURS_UP =               tigetstr("cuu1");
460         CURS_RIGHT =            tigetstr("cuf1");
461         CURS_LEFT =             tigetstr("cub1");
462         if (CURS_LEFT == NULL)
463                 CURS_LEFT =     "\b";
464
465         ENTER_STANDOUT =        tigetstr("smso");
466         EXIT_STANDOUT =         tigetstr("rmso");
467         ENTER_UNDERLINE =       tigetstr("smul");
468         EXIT_UNDERLINE =        tigetstr("rmul");
469         ENTER_DIM =             tigetstr("dim");
470         ENTER_BOLD =            tigetstr("bold");
471         ENTER_REVERSE =         tigetstr("rev");
472         EXIT_ATTRIBUTES =       tigetstr("sgr0");
473
474         if (!ENTER_BOLD && ENTER_REVERSE)
475                 ENTER_BOLD = ENTER_REVERSE;
476         if (!ENTER_BOLD && ENTER_STANDOUT)
477                 ENTER_BOLD = ENTER_STANDOUT;
478         if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
479                 ENTER_UNDERLINE = ENTER_STANDOUT;
480                 EXIT_UNDERLINE = EXIT_STANDOUT;
481         }
482         if (!ENTER_DIM && ENTER_STANDOUT)
483                 ENTER_DIM = ENTER_STANDOUT;
484         if (!ENTER_REVERSE && ENTER_STANDOUT)
485                 ENTER_REVERSE = ENTER_STANDOUT;
486         if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
487                 EXIT_ATTRIBUTES = EXIT_STANDOUT;
488         
489         /*
490          * Note that we use REVERSE for the alternate character set,
491          * not the as/ae capabilities.  This is because we are modelling
492          * the model 37 teletype (since that's what nroff outputs) and
493          * the typical as/ae is more of a graphics set, not the greek
494          * letters the 37 has.
495          */
496
497         UNDER_CHAR =            tigetstr("uc");
498         must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
499 }
500
501 static int curmode = 0;
502
503 void
504 outc(wint_t c, int width) {
505         int i;
506
507         putwchar(c);
508         if (must_use_uc && (curmode&UNDERL)) {
509                 for (i=0; i<width; i++)
510                         PRINT(CURS_LEFT);
511                 for (i=0; i<width; i++)
512                         PRINT(UNDER_CHAR);
513         }
514 }
515
516 void setmode(int newmode)
517 {
518         if (!iflag) {
519                 if (curmode != NORMAL && newmode != NORMAL)
520                         setmode(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 }
566
567 static void
568 setcol(int newcol) {
569         col = newcol;
570
571         if (col < 0)
572                 col = 0;
573         else if (col > maxcol)
574                 needcol(col);
575 }
576
577 static void
578 needcol(int col) {
579         maxcol = col;
580
581         /* If col >= obuflen, expand obuf until obuflen > col. */
582         while (col >= obuflen) {
583                 /* Paranoid check for obuflen == INT_MAX. */
584                 if (obuflen == INT_MAX) {
585                         fprintf(stderr,
586                                 _("Input line too long.\n"));
587                         exit(1);
588                 }
589
590                 /* Similar paranoia: double only up to INT_MAX. */
591                 obuflen = ((INT_MAX / 2) < obuflen)
592                         ? INT_MAX
593                         : obuflen * 2;
594
595                 /* Now we can try to expand obuf. */
596                 obuf = realloc(obuf, sizeof(struct CHAR) * obuflen);
597                 if (obuf == NULL) {
598                         fprintf(stderr,
599                                 _("Out of memory when growing buffer.\n"));
600                         exit(1);
601                 }
602         }
603 }