]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - text-utils/ul.c
docs: update year in libs docs
[thirdparty/util-linux.git] / text-utils / ul.c
index f10d224567152de90554abc3ec8ca6ad7618254f..e8137edbd88278b04b4c5ac480a2636c719abe68 100644 (file)
  */
 
 /*
-**     modified by Kars de Jong <jongk@cs.utwente.nl> to use terminfo instead
-**       of termcap.
-*/
+ * modified by Kars de Jong <jongk@cs.utwente.nl>
+ *     to use terminfo instead of termcap.
+ * 1999-02-22 Arkadiusz Miƛkiewicz <misiek@pld.ORG.PL>
+ *     added Native Language Support
+ * 1999-09-19 Bruno Haible <haible@clisp.cons.org>
+ *     modified to work correctly in multi-byte locales
+ */
 
 #include <stdio.h>
 #include <unistd.h>            /* for getopt(), isatty() */
-#include <string.h>            /* for bzero() */
-#include <term.h>              /* for setupterm() */
+#include <string.h>            /* for memset(), strcpy() */
 #include <stdlib.h>            /* for getenv() */
-
-void filter(FILE *f);
-void flushln(void);
-void overstrike(void);
-void iattr(void);
-void initbuf(void);
-void fwd(void);
-void reverse(void);
-void initinfo(void);
-void outc(int c);
-void setmode(int newmode);
+#include <limits.h>            /* for INT_MAX */
+#include <signal.h>            /* for signal() */
+#include <errno.h>
+#include <getopt.h>
+
+#if defined(HAVE_NCURSESW_TERM_H)
+# include <ncursesw/term.h>
+#elif defined(HAVE_NCURSES_TERM_H)
+# include <ncurses/term.h>
+#elif defined(HAVE_TERM_H)
+# include <term.h>
+#endif
+
+#include "nls.h"
+#include "xalloc.h"
+#include "widechar.h"
+#include "c.h"
+#include "closestream.h"
+
+#ifdef HAVE_WIDECHAR
+/* Output an ASCII character as a wide character */
+static int put1wc(int c)
+{
+       if (putwchar(c) == WEOF)
+               return EOF;
+       else
+               return c;
+}
+#define putwp(s) tputs(s, STDOUT_FILENO, put1wc)
+#else
+#define putwp(s) putp(s)
+#endif
+
+static int handle_escape(FILE * f);
+static void filter(FILE *f);
+static void flushln(void);
+static void overstrike(void);
+static void iattr(void);
+static void initbuf(void);
+static void fwd(void);
+static void reverse(void);
+static void initinfo(void);
+static void outc(wint_t c, int width);
+static void xsetmode(int newmode);
+static void setcol(int newcol);
+static void needcol(int col);
+static void sig_handler(int signo);
+static void print_out(char *line);
 
 #define        IESC    '\033'
 #define        SO      '\016'
@@ -59,7 +99,6 @@ void setmode(int newmode);
 #define        HFWD    '9'
 #define        HREV    '8'
 #define        FREV    '7'
-#define        MAXBUF  512
 
 #define        NORMAL  000
 #define        ALTSET  001     /* Reverse */
@@ -68,228 +107,294 @@ void setmode(int newmode);
 #define        UNDERL  010     /* Ul */
 #define        BOLD    020     /* Bold */
 
-int    must_use_uc, must_overstrike;
-char   *CURS_UP, *CURS_RIGHT, *CURS_LEFT,
-       *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
-       *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
-
-struct CHAR    {
+static int     must_use_uc, must_overstrike;
+static char    *CURS_UP,
+               *CURS_RIGHT,
+               *CURS_LEFT,
+               *ENTER_STANDOUT,
+               *EXIT_STANDOUT,
+               *ENTER_UNDERLINE,
+               *EXIT_UNDERLINE,
+               *ENTER_DIM,
+               *ENTER_BOLD,
+               *ENTER_REVERSE,
+               *UNDER_CHAR,
+               *EXIT_ATTRIBUTES;
+
+struct CHAR {
        char    c_mode;
-       char    c_char;
-} ;
+       wchar_t c_char;
+       int     c_width;
+};
+
+static struct  CHAR    *obuf;
+static int     obuflen;
+static int     col, maxcol;
+static int     mode;
+static int     halfpos;
+static int     upln;
+static int     iflag;
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+       FILE *out = stdout;
+       fputs(USAGE_HEADER, out);
+       fprintf(out, _(" %s [options] [<file> ...]\n"), program_invocation_short_name);
+
+       fputs(USAGE_SEPARATOR, out);
+       fputs(_("Do underlining.\n"), out);
 
-struct CHAR    obuf[MAXBUF];
-int    col, maxcol;
-int    mode;
-int    halfpos;
-int    upln;
-int    iflag;
+       fputs(USAGE_OPTIONS, out);
+       fputs(_(" -t, -T, --terminal TERMINAL  override the TERM environment variable\n"), out);
+       fputs(_(" -i, --indicated              underlining is indicated via a separate line\n"), out);
+       fputs(USAGE_SEPARATOR, out);
+       printf(USAGE_HELP_OPTIONS(30));
 
-#define        PRINT(s)        if (s == NULL) /* void */; else putp(s)
+       printf(USAGE_MAN_TAIL("ul(1)"));
+
+       exit(EXIT_SUCCESS);
+}
 
 int main(int argc, char **argv)
 {
-       extern int optind;
-       extern char *optarg;
-       int c, ret;
+       int c, ret, tflag = 0;
        char *termtype;
        FILE *f;
 
+       static const struct option longopts[] = {
+               { "terminal",   required_argument,      NULL, 't' },
+               { "indicated",  no_argument,            NULL, 'i' },
+               { "version",    no_argument,            NULL, 'V' },
+               { "help",       no_argument,            NULL, 'h' },
+               { NULL, 0, NULL, 0 }
+       };
+
+       setlocale(LC_ALL, "");
+       bindtextdomain(PACKAGE, LOCALEDIR);
+       textdomain(PACKAGE);
+       close_stdout_atexit();
+
+       signal(SIGINT, sig_handler);
+       signal(SIGTERM, sig_handler);
+
        termtype = getenv("TERM");
-       if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
-               termtype = "lpr";
-       while ((c=getopt(argc, argv, "it:T:")) != EOF)
-               switch(c) {
+
+       while ((c = getopt_long(argc, argv, "it:T:Vh", longopts, NULL)) != -1)
+               switch (c) {
 
                case 't':
-               case 'T': /* for nroff compatibility */
-                               termtype = optarg;
+               case 'T':
+                       /* for nroff compatibility */
+                       termtype = optarg;
+                       tflag = 1;
                        break;
                case 'i':
                        iflag = 1;
                        break;
 
+               case 'V':
+                       print_version(EXIT_SUCCESS);
+               case 'h':
+                       usage();
                default:
-                       fprintf(stderr,
-                               "usage: %s [ -i ] [ -tTerm ] file...\n",
-                               argv[0]);
-                       exit(1);
+                       errtryhelp(EXIT_FAILURE);
                }
-       setupterm(termtype, 1, &ret);
-       switch(ret) {
+       setupterm(termtype, STDOUT_FILENO, &ret);
+       switch (ret) {
 
        case 1:
                break;
 
        default:
-               fprintf(stderr,"trouble reading terminfo");
-               /* fall through to ... */
+               warnx(_("trouble reading terminfo"));
+               /* fallthrough */
 
        case 0:
-               /* No such terminal type - assume dumb */
-               setupterm("dumb", 1, (int *)0);
+               if (tflag)
+                       warnx(_("terminal `%s' is not known, defaulting to `dumb'"),
+                               termtype);
+               setupterm("dumb", STDOUT_FILENO, (int *)0);
                break;
        }
        initinfo();
-       if (    (tigetflag("os") && ENTER_BOLD==NULL ) ||
-               (tigetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL))
-                       must_overstrike = 1;
+       if ((tigetflag("os") && ENTER_BOLD==NULL ) ||
+           (tigetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL))
+               must_overstrike = 1;
        initbuf();
        if (optind == argc)
                filter(stdin);
-       else for (; optind<argc; optind++) {
-               f = fopen(argv[optind],"r");
-               if (f == NULL) {
-                       perror(argv[optind]);
-                       exit(1);
-               } else
+       else
+               for (; optind < argc; optind++) {
+                       f = fopen(argv[optind],"r");
+                       if (!f)
+                               err(EXIT_FAILURE, _("cannot open %s"),
+                                   argv[optind]);
                        filter(f);
-       }
-       return 0;
+                       fclose(f);
+               }
+       free(obuf);
+       return EXIT_SUCCESS;
 }
 
-void filter(FILE *f)
+static int handle_escape(FILE * f)
 {
-       register c;
-
-       while ((c = getc(f)) != EOF) switch(c) {
-
-       case '\b':
-               if (col > 0)
-                       col--;
-               continue;
+       wint_t c;
+
+       switch (c = getwc(f)) {
+       case HREV:
+               if (halfpos == 0) {
+                       mode |= SUPERSC;
+                       halfpos--;
+               } else if (halfpos > 0) {
+                       mode &= ~SUBSC;
+                       halfpos--;
+               } else {
+                       halfpos = 0;
+                       reverse();
+               }
+               return 0;
+       case HFWD:
+               if (halfpos == 0) {
+                       mode |= SUBSC;
+                       halfpos++;
+               } else if (halfpos < 0) {
+                       mode &= ~SUPERSC;
+                       halfpos++;
+               } else {
+                       halfpos = 0;
+                       fwd();
+               }
+               return 0;
+       case FREV:
+               reverse();
+               return 0;
+       default:
+               /* unknown escape */
+               ungetwc(c, f);
+               return 1;
+       }
+}
 
-       case '\t':
-               col = (col+8) & ~07;
-               if (col > maxcol)
-                       maxcol = col;
-               continue;
+static void filter(FILE *f)
+{
+       wint_t c;
+       int i, w;
 
-       case '\r':
-               col = 0;
-               continue;
-
-       case SO:
-               mode |= ALTSET;
-               continue;
-
-       case SI:
-               mode &= ~ALTSET;
-               continue;
-
-       case IESC:
-               switch (c = getc(f)) {
-
-               case HREV:
-                       if (halfpos == 0) {
-                               mode |= SUPERSC;
-                               halfpos--;
-                       } else if (halfpos > 0) {
-                               mode &= ~SUBSC;
-                               halfpos--;
-                       } else {
-                               halfpos = 0;
-                               reverse();
+       while ((c = getwc(f)) != WEOF) {
+               switch (c) {
+               case '\b':
+                       setcol(col - 1);
+                       continue;
+               case '\t':
+                       setcol((col + 8) & ~07);
+                       continue;
+               case '\r':
+                       setcol(0);
+                       continue;
+               case SO:
+                       mode |= ALTSET;
+                       continue;
+               case SI:
+                       mode &= ~ALTSET;
+                       continue;
+               case IESC:
+                       if (handle_escape(f)) {
+                               c = getwc(f);
+                               errx(EXIT_FAILURE,
+                                    _("unknown escape sequence in input: %o, %o"), IESC, c);
                        }
                        continue;
-
-               case HFWD:
-                       if (halfpos == 0) {
-                               mode |= SUBSC;
-                               halfpos++;
-                       } else if (halfpos < 0) {
-                               mode &= ~SUPERSC;
-                               halfpos++;
-                       } else {
-                               halfpos = 0;
-                               fwd();
+               case '_':
+                       if (obuf[col].c_char || obuf[col].c_width < 0) {
+                               while (col > 0 && obuf[col].c_width < 0)
+                                       col--;
+                               w = obuf[col].c_width;
+                               for (i = 0; i < w; i++)
+                                       obuf[col++].c_mode |= UNDERL | mode;
+                               setcol(col);
+                               continue;
                        }
+                       obuf[col].c_char = '_';
+                       obuf[col].c_width = 1;
+                       /* fallthrough */
+               case ' ':
+                       setcol(col + 1);
                        continue;
-
-               case FREV:
-                       reverse();
+               case '\n':
+                       flushln();
+                       continue;
+               case '\f':
+                       flushln();
+                       putwchar('\f');
                        continue;
-
                default:
-                       fprintf(stderr,
-                               "Unknown escape sequence in input: %o, %o\n",
-                               IESC, c);
-                       exit(1);
-               }
-               continue;
-
-       case '_':
-               if (obuf[col].c_char)
-                       obuf[col].c_mode |= UNDERL | mode;
-               else
-                       obuf[col].c_char = '_';
-       case ' ':
-               col++;
-               if (col > maxcol)
-                       maxcol = col;
-               continue;
-
-       case '\n':
-               flushln();
-               continue;
-
-       case '\f':
-               flushln();
-               putchar('\f');
-               continue;
-
-       default:
-               if (c < ' ')    /* non printing */
+                       if (!iswprint(c))
+                               /* non printable */
+                               continue;
+                       w = wcwidth(c);
+                       needcol(col + w);
+                       if (obuf[col].c_char == '\0') {
+                               obuf[col].c_char = c;
+                               for (i = 0; i < w; i++)
+                                       obuf[col + i].c_mode = mode;
+                               obuf[col].c_width = w;
+                               for (i = 1; i < w; i++)
+                                       obuf[col + i].c_width = -1;
+                       } else if (obuf[col].c_char == '_') {
+                               obuf[col].c_char = c;
+                               for (i = 0; i < w; i++)
+                                       obuf[col + i].c_mode |= UNDERL | mode;
+                               obuf[col].c_width = w;
+                               for (i = 1; i < w; i++)
+                                       obuf[col + i].c_width = -1;
+                       } else if ((wint_t) obuf[col].c_char == c) {
+                               for (i = 0; i < w; i++)
+                                       obuf[col + i].c_mode |= BOLD | mode;
+                       } else {
+                               w = obuf[col].c_width;
+                               for (i = 0; i < w; i++)
+                                       obuf[col + i].c_mode = mode;
+                       }
+                       setcol(col + w);
                        continue;
-               if (obuf[col].c_char == '\0') {
-                       obuf[col].c_char = c;
-                       obuf[col].c_mode = mode;
-               } else if (obuf[col].c_char == '_') {
-                       obuf[col].c_char = c;
-                       obuf[col].c_mode |= UNDERL|mode;
-               } else if (obuf[col].c_char == c)
-                       obuf[col].c_mode |= BOLD|mode;
-               else
-                       obuf[col].c_mode = mode;
-               col++;
-               if (col > maxcol)
-                       maxcol = col;
-               continue;
+               }
        }
        if (maxcol)
                flushln();
 }
 
-void flushln()
+static void flushln(void)
 {
-       register lastmode;
-       register i;
+       int lastmode;
+       int i;
        int hadmodes = 0;
 
        lastmode = NORMAL;
-       for (i=0; i<maxcol; i++) {
+       for (i = 0; i < maxcol; i++) {
                if (obuf[i].c_mode != lastmode) {
                        hadmodes++;
-                       setmode(obuf[i].c_mode);
+                       xsetmode(obuf[i].c_mode);
                        lastmode = obuf[i].c_mode;
                }
                if (obuf[i].c_char == '\0') {
-                       if (upln)
-                               PRINT(CURS_RIGHT);
-                       else
-                               outc(' ');
+                       if (upln) {
+                               print_out(CURS_RIGHT);
+                       else
+                               outc(' ', 1);
                } else
-                       outc(obuf[i].c_char);
+                       outc(obuf[i].c_char, obuf[i].c_width);
+               if (obuf[i].c_width > 1)
+                       i += obuf[i].c_width - 1;
        }
        if (lastmode != NORMAL) {
-               setmode(0);
+               xsetmode(0);
        }
        if (must_overstrike && hadmodes)
                overstrike();
-       putchar('\n');
+       putwchar('\n');
        if (iflag && hadmodes)
                iattr();
-       (void)fflush(stdout);
+       fflush(stdout);
        if (upln)
                upln--;
        initbuf();
@@ -299,15 +404,15 @@ void flushln()
  * For terminals that can overstrike, overstrike underlines and bolds.
  * We don't do anything with halfline ups and downs, or Greek.
  */
-void overstrike()
+static void overstrike(void)
 {
        register int i;
-       char lbuf[256];
-       register char *cp = lbuf;
+       register wchar_t *lbuf = xcalloc(maxcol + 1, sizeof(wchar_t));
+       register wchar_t *cp = lbuf;
        int hadbold=0;
 
        /* Set up overstrike buffer */
-       for (i=0; i<maxcol; i++)
+       for (i = 0; i < maxcol; i++)
                switch (obuf[i].c_mode) {
                case NORMAL:
                default:
@@ -318,31 +423,33 @@ void overstrike()
                        break;
                case BOLD:
                        *cp++ = obuf[i].c_char;
+                       if (obuf[i].c_width > 1)
+                               i += obuf[i].c_width - 1;
                        hadbold=1;
                        break;
                }
-       putchar('\r');
-       for (*cp=' '; *cp==' '; cp--)
+       putwchar('\r');
+       for (*cp = ' '; *cp == ' '; cp--)
                *cp = 0;
-       for (cp=lbuf; *cp; cp++)
-               putchar(*cp);
+       fputws(lbuf, stdout);
        if (hadbold) {
-               putchar('\r');
-               for (cp=lbuf; *cp; cp++)
-                       putchar(*cp=='_' ? ' ' : *cp);
-               putchar('\r');
-               for (cp=lbuf; *cp; cp++)
-                       putchar(*cp=='_' ? ' ' : *cp);
+               putwchar('\r');
+               for (cp = lbuf; *cp; cp++)
+                       putwchar(*cp == '_' ? ' ' : *cp);
+               putwchar('\r');
+               for (cp = lbuf; *cp; cp++)
+                       putwchar(*cp == '_' ? ' ' : *cp);
        }
+       free(lbuf);
 }
 
-void iattr()
+static void iattr(void)
 {
        register int i;
-       char lbuf[256];
-       register char *cp = lbuf;
+       register wchar_t *lbuf = xcalloc(maxcol + 1, sizeof(wchar_t));
+       register wchar_t *cp = lbuf;
 
-       for (i=0; i<maxcol; i++)
+       for (i = 0; i < maxcol; i++)
                switch (obuf[i].c_mode) {
                case NORMAL:    *cp++ = ' '; break;
                case ALTSET:    *cp++ = 'g'; break;
@@ -352,46 +459,50 @@ void iattr()
                case BOLD:      *cp++ = '!'; break;
                default:        *cp++ = 'X'; break;
                }
-       for (*cp=' '; *cp==' '; cp--)
+       for (*cp = ' '; *cp == ' '; cp--)
                *cp = 0;
-       for (cp=lbuf; *cp; cp++)
-               putchar(*cp);
-       putchar('\n');
+       fputws(lbuf, stdout);
+       putwchar('\n');
+       free(lbuf);
 }
 
-void initbuf()
+static void initbuf(void)
 {
-
-       bzero((char *)obuf, sizeof (obuf));     /* depends on NORMAL == 0 */
-       col = 0;
+       if (obuf == NULL) {
+               /* First time. */
+               obuflen = BUFSIZ;
+               obuf = xcalloc(obuflen, sizeof(struct CHAR));
+       } else
+               /* assumes NORMAL == 0 */
+               memset(obuf, 0, sizeof(struct CHAR) * maxcol);
+
+       setcol(0);
        maxcol = 0;
        mode &= ALTSET;
 }
 
-void fwd()
+static void fwd(void)
 {
-       register oldcol, oldmax;
+       int oldcol, oldmax;
 
        oldcol = col;
        oldmax = maxcol;
        flushln();
-       col = oldcol;
+       setcol(oldcol);
        maxcol = oldmax;
 }
 
-void reverse()
+static void reverse(void)
 {
        upln++;
        fwd();
-       PRINT(CURS_UP);
-       PRINT(CURS_UP);
+       print_out(CURS_UP);
+       print_out(CURS_UP);
        upln++;
 }
 
-void initinfo()
+static void initinfo(void)
 {
-       char *getenv(), *tigetstr();
-
        CURS_UP =               tigetstr("cuu1");
        CURS_RIGHT =            tigetstr("cuf1");
        CURS_LEFT =             tigetstr("cub1");
@@ -421,10 +532,10 @@ void initinfo()
                ENTER_REVERSE = ENTER_STANDOUT;
        if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
                EXIT_ATTRIBUTES = EXIT_STANDOUT;
-       
+
        /*
         * Note that we use REVERSE for the alternate character set,
-        * not the as/ae capabilities.  This is because we are modelling
+        * not the as/ae capabilities.  This is because we are modeling
         * the model 37 teletype (since that's what nroff outputs) and
         * the typical as/ae is more of a graphics set, not the greek
         * letters the 37 has.
@@ -436,62 +547,107 @@ void initinfo()
 
 static int curmode = 0;
 
-void outc(int c)
-{
-       putchar(c);
+static void outc(wint_t c, int width) {
+       int i;
+
+       putwchar(c);
        if (must_use_uc && (curmode&UNDERL)) {
-               PRINT(CURS_LEFT);
-               PRINT(UNDER_CHAR);
+               for (i = 0; i < width; i++)
+                       print_out(CURS_LEFT);
+               for (i = 0; i < width; i++)
+                       print_out(UNDER_CHAR);
        }
 }
 
-void setmode(int newmode)
+static void xsetmode(int newmode)
 {
        if (!iflag) {
                if (curmode != NORMAL && newmode != NORMAL)
-                       setmode(NORMAL);
+                       xsetmode(NORMAL);
                switch (newmode) {
                case NORMAL:
-                       switch(curmode) {
+                       switch (curmode) {
                        case NORMAL:
                                break;
                        case UNDERL:
-                               PRINT(EXIT_UNDERLINE);
+                               print_out(EXIT_UNDERLINE);
                                break;
                        default:
                                /* This includes standout */
-                               PRINT(EXIT_ATTRIBUTES);
+                               print_out(EXIT_ATTRIBUTES);
                                break;
                        }
                        break;
                case ALTSET:
-                       PRINT(ENTER_REVERSE);
+                       print_out(ENTER_REVERSE);
                        break;
                case SUPERSC:
                        /*
                         * This only works on a few terminals.
                         * It should be fixed.
                         */
-                       PRINT(ENTER_UNDERLINE);
-                       PRINT(ENTER_DIM);
+                       print_out(ENTER_UNDERLINE);
+                       print_out(ENTER_DIM);
                        break;
                case SUBSC:
-                       PRINT(ENTER_DIM);
+                       print_out(ENTER_DIM);
                        break;
                case UNDERL:
-                       PRINT(ENTER_UNDERLINE);
+                       print_out(ENTER_UNDERLINE);
                        break;
                case BOLD:
-                       PRINT(ENTER_BOLD);
+                       print_out(ENTER_BOLD);
                        break;
                default:
                        /*
                         * We should have some provision here for multiple modes
                         * on at once.  This will have to come later.
                         */
-                       PRINT(ENTER_STANDOUT);
+                       print_out(ENTER_STANDOUT);
                        break;
                }
        }
        curmode = newmode;
 }
+
+static void setcol(int newcol) {
+       col = newcol;
+
+       if (col < 0)
+               col = 0;
+       else if (col > maxcol)
+               needcol(col);
+}
+
+static void needcol(int acol) {
+       maxcol = acol;
+
+       /* If col >= obuflen, expand obuf until obuflen > col. */
+       while (acol >= obuflen) {
+               /* Paranoid check for obuflen == INT_MAX. */
+               if (obuflen == INT_MAX)
+                       errx(EXIT_FAILURE, _("Input line too long."));
+
+               /* Similar paranoia: double only up to INT_MAX. */
+               if (obuflen < (INT_MAX / 2))
+                       obuflen *= 2;
+               else
+                       obuflen = INT_MAX;
+
+               /* Now we can try to expand obuf. */
+               obuf = xrealloc(obuf, sizeof(struct CHAR) * obuflen);
+       }
+}
+
+static void sig_handler(int signo __attribute__ ((__unused__)))
+{
+       _exit(EXIT_SUCCESS);
+}
+
+static void print_out(char *line)
+{
+       if (line == NULL)
+               return;
+
+       putwp(line);
+}