Much smarter version dealing with attributes (MRH)
authorBill Joy <root@ucbvax.Berkeley.EDU>
Mon, 23 Nov 1981 09:02:28 +0000 (01:02 -0800)
committerBill Joy <root@ucbvax.Berkeley.EDU>
Mon, 23 Nov 1981 09:02:28 +0000 (01:02 -0800)
SCCS-vsn: usr.bin/ul/ul.c 4.2

usr/src/usr.bin/ul/ul.c

index 779927e..5a0790c 100644 (file)
@@ -1,81 +1,76 @@
-static char *sccsid = "@(#)ul.c        4.1 (Berkeley) %G%";
-/*
- * ul
- */
+/*     @(#)vcrt.c      3.13    */
 #include <stdio.h>
 
 #include <stdio.h>
 
+#define        IESC    '\033'
+#define        SO      '\016'
+#define        SI      '\017'
+#define        HFWD    '9'
+#define        HREV    '8'
+#define        FREV    '7'
+#define        MAXBUF  512
+
+#define        NORMAL  000
+#define        ALTSET  001     /* Reverse */
+#define        SUPERSC 002     /* Dim */
+#define        SUBSC   004     /* Dim | Ul */
+#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    {
+       char    c_mode;
+       char    c_char;
+} ;
+
 char   buf[BUFSIZ];
 char   buf[BUFSIZ];
-char   isul[BUFSIZ];
-char   termcap[1024];
-char   ulbuf[BUFSIZ];
-char   *stul, *endul, *chul;
-char   *backspace;
-char   *termtype;
-int    outc();
-char   *tgetstr();
-char   *getenv();
+
+struct CHAR    obuf[MAXBUF];
+int    col, maxcol;
+int    mode;
+int    halfpos;
+int    upln;
+int    iflag;
 
 main(argc, argv)
        int argc;
        char **argv;
 {
 
 main(argc, argv)
        int argc;
        char **argv;
 {
-       register int i;
-       char *cp;
+       int c;
+       char *cp, *termtype;
        FILE *f;
        FILE *f;
+       char termcap[1024];
+       char *getenv();
+       extern int optind;
+       extern char *optarg;
 
 
-       argc--, argv++;
        termtype = getenv("TERM");
        termtype = getenv("TERM");
-       if (termtype == NULL)
-               termtype = "dumb";
-       while (argc > 0 && argv[0][0] == '-') {
-               switch(argv[0][1]) {
+       if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
+               termtype = "lpr";
+       while ((c=getopt(argc, argv, "it:T:")) != EOF)
+               switch(c) {
 
                case 't':
                case 'T': /* for nroff compatibility */
 
                case 't':
                case 'T': /* for nroff compatibility */
-                       if (argv[0][2])
-                               termtype = &argv[0][2];
-                       else {
-                               termtype = argv[1];
-                               argc--;
-                               argv++;
-                       }
+                               termtype = optarg;
                        break;
                case 'i':
                        break;
                case 'i':
-                       argc--, argv++;
-                       iul(argc, argv);
-                       exit(0);
+                       iflag = 1;
+                       break;
 
                default:
 
                default:
-                       printf("Usage: ul [ -i ] [ -tTerm ] file...\n");
+                       fprintf(stderr,
+                               "Usage: %s [ -i ] [ -tTerm ] file...\n",
+                               argv[0]);
                        exit(1);
                }
                        exit(1);
                }
-       }
+
        switch(tgetent(termcap, termtype)) {
 
        case 1:
        switch(tgetent(termcap, termtype)) {
 
        case 1:
-               if (tgetflag("os"))
-                       execv("/bin/cat",argv);
-               cp = ulbuf;
-               if ((backspace = tgetstr("bc",&cp)) == NULL)
-                       backspace = "\b";
-               /*
-                * Handle terminals that have start underline/stop
-                * underline sequences, as well as those with
-                * underline char sequences (we assume the sequence
-                * moves the cursor forward one character).
-                * If we can't find underline sequences, we
-                * settle for standout sequences.
-                */
-               if ((chul=tgetstr("uc",&cp)) == NULL)
-                       chul = "";
-               if ((stul=tgetstr("us",&cp)) == NULL && !tgetflag("ul") &&
-                   (!*chul) && (stul=tgetstr("so",&cp)) == NULL)
-                       stul = "";
-               if ((endul=tgetstr("ue",&cp)) == NULL && !tgetflag("ul") &&
-                   (!*chul) && (endul=tgetstr("se",&cp)) == NULL)
-                       endul = "";
-               if (chul==0&&stul==0&&endul==0&&tgetflag("ul"))
-                       execv("/bin/cat",argv);
                break;
 
        default:
                break;
 
        default:
@@ -84,15 +79,21 @@ main(argc, argv)
 
        case 0:
                /* No such terminal type - assume dumb */
 
        case 0:
                /* No such terminal type - assume dumb */
-               stul = endul = chul = "";
+               strcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:");
                break;
        }
                break;
        }
-       if (argc == 0)
+       initcap();
+       if (    (tgetflag("os") && ENTER_BOLD==NULL ) ||
+               (tgetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL))
+                       must_overstrike = 1;
+       setbuf(stdout, buf);
+       initbuf();
+       if (optind == argc)
                filter(stdin);
                filter(stdin);
-       else for (i=0; i<argc; i++) {
-               f = fopen(argv[i],"r");
+       else for (; optind<argc; optind++) {
+               f = fopen(argv[optind],"r");
                if (f == NULL) {
                if (f == NULL) {
-                       perror(argv[i]);
+                       perror(argv[optind]);
                        exit(1);
                } else
                        filter(f);
                        exit(1);
                } else
                        filter(f);
@@ -103,184 +104,425 @@ main(argc, argv)
 filter(f)
 FILE *f;
 {
 filter(f)
 FILE *f;
 {
-       register int p, n;
-       register char c;
-       int state;
-
-       n = 0;
-       for (;;) {
-               p = 0;
-               for (p=0; p<n; p++) {
-                       buf[p] = '\0';
-                       isul[p] = 0;
-               }
-               p = n = 0;
+       register c;
 
 
-               for (;;) {
-                       c = getc(f);
-                       if (c==EOF)
-                               break;
-                       if (c=='\b') {
-                               if (p > 0) {
-                                       p--;
-                               }
-                       } else if (c=='_' && isul[p]==0 && buf[p]) {
-                               isul[p] = 1;
-                               p++;
+       while((c = getc(f)) != EOF) switch(c) {
+
+       case '\b':
+               if (col > 0)
+                       col--;
+               continue;
+
+       case '\t':
+               col = (col+8) & ~07;
+               if (col > maxcol)
+                       maxcol = col;
+               continue;
+
+       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 {
                        } else {
-                               if (buf[p] == '_')
-                                       isul[p] = 1;
-                               buf[p] = c;
-                               p++;
-                               if (n < p)
-                                       n = p;
+                               halfpos = 0;
+                               reverse();
                        }
                        }
-                       if (c=='\n')
-                               break;
-               }
+                       continue;
 
 
-               state = 0;
-               for (p=0; p<n; p++) {
-                       if (isul[p] != state)
-                               tputs(isul[p] ? stul : endul, 1, outc);
-                       state = isul[p];
-                       putchar(buf[p]);
-                       if (isul[p] && *chul) {
-                               printf("%s",backspace);
-                               tputs(chul, 1, outc);
+               case HFWD:
+                       if (halfpos == 0) {
+                               mode |= SUBSC;
+                               halfpos++;
+                       } else if (halfpos < 0) {
+                               mode &= ~SUPERSC;
+                               halfpos++;
+                       } else {
+                               halfpos = 0;
+                               fwd();
                        }
                        }
+                       continue;
+
+               case FREV:
+                       reverse();
+                       continue;
+
+               default:
+                       fprintf(stderr,
+                               "Unknown escape sequence in input: %o, %o\n",
+                               IESC, c);
+                       exit(1);
                }
                }
-               if (c==EOF) break;
+               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();
+
+       default:
+               if (c < ' ')    /* non printing */
+                       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
+                       obuf[col].c_mode |= BOLD|mode;
+               col++;
+               if (col > maxcol)
+                       maxcol = col;
+               continue;
        }
        }
+       flushln();
 }
 
 }
 
-outc(c)
-char c;
+flushln()
 {
 {
-       putchar(c);
+       register lastmode;
+       register i;
+       int hadmodes = 0;
+
+       lastmode = NORMAL;
+       for (i=0; i<maxcol; i++) {
+               if (obuf[i].c_mode != lastmode) {
+                       hadmodes++;
+                       setmode(obuf[i].c_mode);
+                       lastmode = obuf[i].c_mode;
+               }
+               if (obuf[i].c_char == '\0') {
+                       if (upln) {
+                               puts(CURS_RIGHT);
+                       } else
+                               outc(' ');
+               } else
+                       outc(obuf[i].c_char);
+       }
+       if (lastmode != NORMAL) {
+               setmode(0);
+       }
+       if (must_overstrike && hadmodes)
+               overstrike();
+       putchar('\n');
+       if (iflag && hadmodes)
+               iattr();
+       fflush(stdout);
+       if (upln)
+               upln--;
+       initbuf();
 }
 
 }
 
-#define BACKSPACE 0
-#define        QUOTE   0200
+/*
+ * For terminals that can overstrike, overstrike underlines and bolds.
+ * We don't do anything with halfline ups and downs, or Greek.
+ */
+overstrike()
+{
+       register int i;
+       char lbuf[256];
+       register char *cp = lbuf;
+       int hadbold=0;
 
 
-char   linebuf[BUFSIZ], genbuf[BUFSIZ];
-char   *strcpy();
+       /* Set up overstrike buffer */
+       for (i=0; i<maxcol; i++)
+               switch (obuf[i].c_mode) {
+               case NORMAL:
+               default:
+                       *cp++ = ' ';
+                       break;
+               case UNDERL:
+                       *cp++ = '_';
+                       break;
+               case BOLD:
+                       *cp++ = obuf[i].c_char;
+                       hadbold=1;
+                       break;
+               }
+       putchar('\r');
+       for (*cp=' '; *cp==' '; cp--)
+               *cp = 0;
+       for (cp=lbuf; *cp; cp++)
+               putchar(*cp);
+       if (hadbold) {
+               putchar('\r');
+               for (cp=lbuf; *cp; cp++)
+                       putchar(*cp=='_' ? ' ' : *cp);
+               putchar('\r');
+               for (cp=lbuf; *cp; cp++)
+                       putchar(*cp=='_' ? ' ' : *cp);
+       }
+}
 
 
-iul(argc, argv)
-       int argc;
-       char *argv[];
+iattr()
 {
 {
-       register c;
-       register char *lp;
+       register int i;
+       char lbuf[256];
+       register char *cp = lbuf;
 
 
-       do {
-               if (argc > 0) {
-                       if (freopen(argv[0], "r", stdin) == NULL) {
-                               perror(argv[0]);
-                               exit(1);
-                       }
-                       argc--; argv++;
-               }
-               while (fgets(linebuf, sizeof linebuf, stdin) != 0) {
-                       for (lp = linebuf; *lp; lp++)
-                               continue;
-                       *--lp = 0;
-                       doulg();
-                       dographic();
-                       if (genbuf[0])
-                               printf("\n%s", genbuf);
-                       putchar('\n');
-                       fflush(stdout);
+       for (i=0; i<maxcol; i++)
+               switch (obuf[i].c_mode) {
+               case NORMAL:    *cp++ = ' '; break;
+               case ALTSET:    *cp++ = 'g'; break;
+               case SUPERSC:   *cp++ = '^'; break;
+               case SUBSC:     *cp++ = 'v'; break;
+               case UNDERL:    *cp++ = '_'; break;
+               case BOLD:      *cp++ = '!'; break;
+               default:        *cp++ = 'X'; break;
                }
                }
-       } while (argc > 0);
-       exit(0);
+       for (*cp=' '; *cp==' '; cp--)
+               *cp = 0;
+       for (cp=lbuf; *cp; cp++)
+               putchar(*cp);
+       putchar('\n');
 }
 
 }
 
-dographic()
+initbuf()
 {
 {
-       register char *lp;
-       register c;
+       register i;
 
 
-       for (lp = linebuf; c = *lp++;) {
-               switch (c) {
-                       case '\b':
-                               if (BACKSPACE == 0)
-                                       c = '?';
-                               break;
-                       default:
-                               if (c < ' ' || c == 0177)
-                                       c = '?';
-                               break;
-                       case '\t':
-                               break;
-               }
-               putchar(c);
+       for (i=0; i<MAXBUF; i++) {
+               obuf[i].c_char = '\0';
+               obuf[i].c_mode = NORMAL;
        }
        }
+       col = 0;
+       maxcol = 0;
+       mode &= ALTSET;
 }
 
 }
 
-doulg()
+fwd()
 {
 {
-       register char *lp, *gp;
-       char *maxgp;
-       register c;
-       char csw;
-       int col;
+       register oldcol, oldmax;
 
 
-       gp = genbuf;
-       *gp = 0;
-       maxgp = gp;
-       col = 0;
-       for (lp = linebuf; c = *lp++;) {
-               switch (c) {
-                       case '\t':
-                               while ((col & 7) != 7) {
-                                       *gp++ = ' ';
-                                       if (gp >= &genbuf[BUFSIZ - 2])
-                                               goto ovflo;
-                                       col++;
-                               }
+       oldcol = col;
+       oldmax = maxcol;
+       flushln();
+       col = oldcol;
+       maxcol = oldmax;
+}
+
+reverse()
+{
+       upln++;
+       fwd();
+       puts(CURS_UP);
+       puts(CURS_UP);
+       upln++;
+}
+
+initcap()
+{
+       static char tcapbuf[512];
+       char *termtype;
+       char *bp = tcapbuf;
+       char *getenv(), *tgetstr();
+
+       /* This nonsense attempts to work with both old and new termcap */
+       CURS_UP =               tgetstr("up", &bp);
+       CURS_RIGHT =            tgetstr("ri", &bp);
+       if (CURS_RIGHT == NULL)
+               CURS_RIGHT =    tgetstr("nd", &bp);
+       CURS_LEFT =             tgetstr("le", &bp);
+       if (CURS_LEFT == NULL)
+               CURS_LEFT =     tgetstr("bc", &bp);
+       if (CURS_LEFT == NULL && tgetflag("bs"))
+               CURS_LEFT =     "\b";
+
+       ENTER_STANDOUT =        tgetstr("so", &bp);
+       EXIT_STANDOUT =         tgetstr("se", &bp);
+       ENTER_UNDERLINE =       tgetstr("us", &bp);
+       EXIT_UNDERLINE =        tgetstr("ue", &bp);
+       ENTER_DIM =             tgetstr("mh", &bp);
+       ENTER_BOLD =            tgetstr("md", &bp);
+       ENTER_REVERSE =         tgetstr("mr", &bp);
+       EXIT_ATTRIBUTES =       tgetstr("me", &bp);
+
+       if (!ENTER_BOLD && ENTER_REVERSE)
+               ENTER_BOLD = ENTER_REVERSE;
+       if (!ENTER_BOLD && ENTER_STANDOUT)
+               ENTER_BOLD = ENTER_STANDOUT;
+       if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
+               ENTER_UNDERLINE = ENTER_STANDOUT;
+               EXIT_UNDERLINE = EXIT_STANDOUT;
+       }
+       if (!ENTER_DIM && ENTER_STANDOUT)
+               ENTER_DIM = ENTER_STANDOUT;
+       if (!ENTER_REVERSE && ENTER_STANDOUT)
+               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
+        * 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.
+        */
+
+#ifdef notdef
+printf("so %s se %s us %s ue %s me %s\n",
+       ENTER_STANDOUT, EXIT_STANDOUT, ENTER_UNDERLINE,
+       EXIT_UNDERLINE, EXIT_ATTRIBUTES);
+#endif
+       UNDER_CHAR =            tgetstr("uc", &bp);
+       must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
+}
+
+outchar(c)
+char c;
+{
+       putchar(c&0177);
+}
+
+puts(str)
+char *str;
+{
+       if (str)
+               tputs(str, 1, outchar);
+}
+
+static curmode = 0;
+outc(c)
+char c;
+{
+       putchar(c);
+       if (must_use_uc &&  (curmode&UNDERL)) {
+               puts(CURS_LEFT);
+               puts(UNDER_CHAR);
+       }
+}
+
+setmode(newmode)
+int newmode;
+{
+       if (!iflag)
+               switch (newmode) {
+               case NORMAL:
+                       switch(curmode) {
+                       case NORMAL:
                                break;
                                break;
-                       default:
-                               if (gp >= maxgp)
-                                       break;
-                               c |= (*gp & QUOTE);
+                       case UNDERL:
+                               puts(EXIT_UNDERLINE);
                                break;
                                break;
-                       case '_':
-                               if (gp >= maxgp)
-                                       c = QUOTE;
-                               else
-                                       c = *gp | QUOTE;
+                       default:
+                               /* This includes standout */
+                               puts(EXIT_ATTRIBUTES);
                                break;
                                break;
-                       case '\b':
-                               if (gp > genbuf) {
-                                       gp--;
-                                       col--;
-                               }
-                               continue;
+                       }
+                       break;
+               case ALTSET:
+                       puts(ENTER_REVERSE);
+                       break;
+               case SUPERSC:
+                       /*
+                        * This only works on a few terminals.
+                        * It should be fixed.
+                        */
+                       puts(ENTER_UNDERLINE);
+                       puts(ENTER_DIM);
+                       break;
+               case SUBSC:
+                       puts(ENTER_DIM);
+                       break;
+               case UNDERL:
+                       puts(ENTER_UNDERLINE);
+                       break;
+               case BOLD:
+                       puts(ENTER_BOLD);
+                       break;
+               default:
+                       /*
+                        * We should have some provision here for multiple modes
+                        * on at once.  This will have to come later.
+                        */
+                       puts(ENTER_STANDOUT);
+                       break;
                }
                }
-               if (gp >= &genbuf[BUFSIZ - 2]) {
-ovflo:
-                       fprintf(stderr, "Line too long\n");
-                       exit(1);
+       curmode = newmode;
+}
+/*     @(#)getopt.c    3.2     */
+#define        ERR(s, c)       if(opterr){\
+       fputs(argv[0], stderr);\
+       fputs(s, stderr);\
+       fputc(c, stderr);\
+       fputc('\n', stderr);}
+
+int    opterr = 1;
+int    optind = 1;
+char   *optarg;
+char   *index();
+
+int
+getopt (argc, argv, opts)
+       char **argv, *opts;
+{
+       static int sp = 1;
+       char c;
+       char *cp;
+
+       if (sp == 1)
+               if (optind >= argc ||
+                  argv[optind][0] != '-' || argv[optind][1] == '\0')
+                       return EOF;
+               else if (strcmp(argv[optind], "--") == NULL) {
+                       optind++;
+                       return EOF;
                }
                }
-               *gp++ = c;
-               if (gp > maxgp)
-                       maxgp = gp;
-               col++;
+               else if (strcmp(argv[optind], "-?") == NULL) {
+                       optind++;
+                       return '?';
+               }
+       c = argv[optind][sp];
+       if (c == ':' || (cp=index(opts, c)) == NULL) {
+               ERR (": illegal option -- ", c);
+               if (argv[optind][++sp] == '\0') {
+                       optind++;
+                       sp = 1;
+               }
+               return '?';
        }
        }
-       *maxgp = 0;
-       strcpy(linebuf, genbuf);
-       for (lp = linebuf, gp = genbuf; c = *lp; gp++, lp++)
-               if (c & QUOTE) {
-                       c &= 0177;
-                       if (c == 0)
-                               *lp = '_', *gp = ' ';
-                       else
-                               *lp = c, *gp = '-';
+       if (*++cp == ':') {
+               if (argv[optind][2] != '\0')
+                       optarg = &argv[optind++][sp+1];
+               else if (++optind >= argc) {
+                       ERR (": option requires an argument -- ", c);
+                       sp = 1;
+                       return '?';
                } else
                } else
-                       *gp = ' ';
-       --gp;
-       while (gp >= genbuf && *gp == ' ')
-               --gp;
-       gp[1] = 0;
+                       optarg = argv[optind++];
+               sp = 1;
+       }
+       else if (argv[optind][++sp] == '\0') {
+               sp = 1;
+               optind++;
+       }
+       return c;
 }
 }