JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Add error control to iofile
[st.git] / st.c
diff --git a/st.c b/st.c
index 8e25d23..e19cefd 100644 (file)
--- a/st.c
+++ b/st.c
@@ -72,6 +72,8 @@
 #define X2COL(x) (((x) - BORDER)/xw.cw)
 #define Y2ROW(y) (((y) - BORDER)/xw.ch)
 
+#define VT102ID "\033[?6c"
+
 enum glyph_attribute {
        ATTR_NULL      = 0,
        ATTR_REVERSE   = 1,
@@ -121,6 +123,7 @@ enum escape_state {
        ESC_STR = 4, /* DSC, OSC, PM, APC */
        ESC_ALTCHARSET = 8,
        ESC_STR_END    = 16, /* a final string was encountered */
+       ESC_TEST       = 32, /* Enter in test mode */
 };
 
 enum window_state {
@@ -287,7 +290,7 @@ static int tresize(int, int);
 static void tscrollup(int, int);
 static void tscrolldown(int, int);
 static void tsetattr(int*, int);
-static void tsetchar(char*);
+static void tsetchar(char *, Glyph *, int, int);
 static void tsetscroll(int, int);
 static void tswapscreen(void);
 static void tsetdirt(int, int);
@@ -337,6 +340,7 @@ static int utf8encode(long *, char *);
 static int utf8size(char *);
 static int isfullutf8(char *, int);
 
+static ssize_t xwrite(int, char *, size_t);
 static void *xmalloc(size_t);
 static void *xrealloc(void *, size_t);
 static void *xcalloc(size_t nmemb, size_t size);
@@ -376,6 +380,21 @@ static char *opt_embed = NULL;
 static char *opt_class = NULL;
 static char *opt_font = NULL;
 
+
+ssize_t
+xwrite(int fd, char *s, size_t len) {
+       size_t aux = len;
+
+       while(len > 0) {
+               ssize_t r = write(fd, s, len);
+               if(r < 0)
+                       return r;
+               len -= r;
+               s += r;
+       }
+       return aux;
+}
+
 void *
 xmalloc(size_t len) {
        void *p = malloc(len);
@@ -875,7 +894,7 @@ execsh(void) {
 
        DEFAULT(envshell, SHELL);
        putenv("TERM="TNAME);
-       args = opt_cmd ? opt_cmd : (char*[]){envshell, "-i", NULL};
+       args = opt_cmd ? opt_cmd : (char *[]){envshell, "-i", NULL};
        execvp(args[0], args);
        exit(EXIT_FAILURE);
 }
@@ -923,13 +942,12 @@ ttynew(void) {
                cmdfd = m;
                signal(SIGCHLD, sigchld);
                if(opt_io) {
-                       if(!strcmp(opt_io, "-")) {
-                               iofd = STDOUT_FILENO;
-                       } else {
-                               if((iofd = open(opt_io, O_WRONLY | O_CREAT, 0666)) < 0) {
-                                       fprintf(stderr, "Error opening %s:%s\n",
-                                               opt_io, strerror(errno));
-                               }
+                       iofd = (!strcmp(opt_io, "-")) ?
+                                 STDOUT_FILENO :
+                                 open(opt_io, O_WRONLY | O_CREAT, 0666);
+                       if(iofd < 0) {
+                               fprintf(stderr, "Error opening %s:%s\n",
+                                       opt_io, strerror(errno));
                        }
                }
        }
@@ -1180,8 +1198,8 @@ tmoveto(int x, int y) {
 }
 
 void
-tsetchar(char *c) {
-       char *vt100_0[62] = { /* 0x41 - 0x7e */
+tsetchar(char *c, Glyph *attr, int x, int y) {
+       static char *vt100_0[62] = { /* 0x41 - 0x7e */
                "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */
                0, 0, 0, 0, 0, 0, 0, 0, /* H - O */
                0, 0, 0, 0, 0, 0, 0, 0, /* P - W */
@@ -1195,17 +1213,17 @@ tsetchar(char *c) {
        /*
         * The table is proudly stolen from rxvt.
         */
-       if(term.c.attr.mode & ATTR_GFX) {
+       if(attr->mode & ATTR_GFX) {
                if(c[0] >= 0x41 && c[0] <= 0x7e
                                && vt100_0[c[0] - 0x41]) {
                        c = vt100_0[c[0] - 0x41];
                }
        }
 
-       term.dirty[term.c.y] = 1;
-       term.line[term.c.y][term.c.x] = term.c.attr;
-       memcpy(term.line[term.c.y][term.c.x].c, c, UTF_SIZ);
-       term.line[term.c.y][term.c.x].state |= GLYPH_SET;
+       term.dirty[y] = 1;
+       term.line[y][x] = *attr;
+       memcpy(term.line[y][x].c, c, UTF_SIZ);
+       term.line[y][x].state |= GLYPH_SET;
 }
 
 void
@@ -1510,6 +1528,10 @@ csihandle(void) {
                DEFAULT(csiescseq.arg[0], 1);
                tmoveto(term.c.x, term.c.y+csiescseq.arg[0]);
                break;
+       case 'c': /* DA -- Device Attributes */
+               if(csiescseq.arg[0] == 0)
+                       ttywrite(VT102ID, sizeof(VT102ID) - 1);
+               break;
        case 'C': /* CUF -- Cursor <n> Forward */
        case 'a':
                DEFAULT(csiescseq.arg[0], 1);
@@ -1784,53 +1806,86 @@ tputtab(bool forward) {
 void
 tputc(char *c, int len) {
        uchar ascii = *c;
-
-       if(iofd != -1)
-               write(iofd, c, len);
-
-       switch(ascii) {
-       case '\t':      /* HT */
-               tputtab(1);
-               return;
-       case '\b':      /* BS */
-               tmoveto(term.c.x-1, term.c.y);
-               return;
-       case '\r':      /* CR */
-               tmoveto(0, term.c.y);
-               return;
-       case '\f':      /* LF */
-       case '\v':      /* VT */
-       case '\n':      /* LF */
-               /* go to first col if the mode is set */
-               tnewline(IS_SET(MODE_CRLF));
-               return;
-       case '\a':      /* BEL */
-               if(term.esc & ESC_STR)
+       bool control = ascii < '\x20' || ascii == 0177;
+
+       if(iofd != -1) {
+               if (xwrite(iofd, c, len) < 0) {
+                       fprintf(stderr, "Error writting in %s:%s\n",
+                               opt_io, strerror(errno));
+                       close(iofd);
+                       iofd = -1;
+               }
+       }
+       /*
+        * STR sequences must be checked before of anything
+        * because it can use some control codes as part of the sequence
+        */
+       if(term.esc & ESC_STR) {
+               switch(ascii) {
+               case '\033':
+                       term.esc = ESC_START | ESC_STR_END;
                        break;
-               if(!(xw.state & WIN_FOCUSED))
-                       xseturgency(1);
-               return;
-       case '\033':    /* ESC */
-               csireset();
-               term.esc = ESC_START;
-               return;
-       case '\016':    /* XXX: SO */
-       case '\017':    /* XXX: SI */
-               break;
-       case '\032':    /* SUB */
-       case '\030':    /* CAN */
-               csireset();
+               case '\a': /* backwards compatibility to xterm */
+                       term.esc = 0;
+                       strhandle();
+                       break;
+               default:
+                       strescseq.buf[strescseq.len++] = ascii;
+                       if(strescseq.len+1 >= STR_BUF_SIZ) {
+                               term.esc = 0;
+                               strhandle();
+                       }
+               }
                return;
-       default:
-       /* case '\005': ENQ (IGNORED) */
-       /* case '\000': NUL (IGNORED) */
-       /* case '\021': XON (IGNORED) */
-       /* case '\023': XOFF (IGNORED) */
-       /* case 0177:   DEL (IGNORED) */
-               break;
        }
-
-       if(term.esc & ESC_START) {
+       /*
+        * Actions of control codes must be performed as soon they arrive
+        * because they can be embedded inside a control sequence, and
+        * they must not cause conflicts with sequences.
+        */
+       if(control) {
+               switch(ascii) {
+               case '\t':      /* HT */
+                       tputtab(1);
+                       return;
+               case '\b':      /* BS */
+                       tmoveto(term.c.x-1, term.c.y);
+                       return;
+               case '\r':      /* CR */
+                       tmoveto(0, term.c.y);
+                       return;
+               case '\f':      /* LF */
+               case '\v':      /* VT */
+               case '\n':      /* LF */
+                       /* go to first col if the mode is set */
+                       tnewline(IS_SET(MODE_CRLF));
+                       return;
+               case '\a':      /* BEL */
+                       if(!(xw.state & WIN_FOCUSED))
+                               xseturgency(1);
+                       return;
+               case '\033':    /* ESC */
+                       csireset();
+                       term.esc = ESC_START;
+                       return;
+               case '\016':    /* SO */
+                       term.c.attr.mode |= ATTR_GFX;
+                       return;
+               case '\017':    /* SI */
+                       term.c.attr.mode &= ~ATTR_GFX;
+                       return;
+               case '\032':    /* SUB */
+               case '\030':    /* CAN */
+                       csireset();
+                       return;
+                case '\005':   /* ENQ (IGNORED) */
+                case '\000':   /* NUL (IGNORED) */
+                case '\021':   /* XON (IGNORED) */
+                case '\023':   /* XOFF (IGNORED) */
+                case 0177:     /* DEL (IGNORED) */
+                       return;
+               }
+       } else if(term.esc & ESC_START) {
                if(term.esc & ESC_CSI) {
                        csiescseq.buf[csiescseq.len++] = ascii;
                        if(BETWEEN(ascii, 0x40, 0x7E)
@@ -1838,22 +1893,6 @@ tputc(char *c, int len) {
                                term.esc = 0;
                                csiparse(), csihandle();
                        }
-               } else if(term.esc & ESC_STR) {
-                       switch(ascii) {
-                       case '\033':
-                               term.esc = ESC_START | ESC_STR_END;
-                               break;
-                       case '\a': /* backwards compatibility to xterm */
-                               term.esc = 0;
-                               strhandle();
-                               break;
-                       default:
-                               strescseq.buf[strescseq.len++] = ascii;
-                               if(strescseq.len+1 >= STR_BUF_SIZ) {
-                                       term.esc = 0;
-                                       strhandle();
-                               }
-                       }
                } else if(term.esc & ESC_STR_END) {
                        term.esc = 0;
                        if(ascii == '\\')
@@ -1876,11 +1915,25 @@ tputc(char *c, int len) {
                                fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii);
                        }
                        term.esc = 0;
+               } else if(term.esc & ESC_TEST) {
+                       if(ascii == '8') { /* DEC screen alignment test. */
+                               char E[UTF_SIZ] = "E";
+                               int x, y;
+
+                               for(x = 0; x < term.col; ++x) {
+                                       for(y = 0; y < term.row; ++y)
+                                               tsetchar(E, &term.c.attr, x, y);
+                               }
+                       }
+                       term.esc = 0;
                } else {
                        switch(ascii) {
                        case '[':
                                term.esc |= ESC_CSI;
                                break;
+                       case '#':
+                               term.esc |= ESC_TEST;
+                               break;
                        case 'P': /* DCS -- Device Control String */
                        case '_': /* APC -- Application Program Command */
                        case '^': /* PM -- Privacy Message */
@@ -1922,6 +1975,10 @@ tputc(char *c, int len) {
                                }
                                term.esc = 0;
                                break;
+                       case 'Z': /* DECID -- Identify Terminal */
+                               ttywrite(VT102ID, sizeof(VT102ID) - 1);
+                               term.esc = 0;
+                               break;
                        case 'c': /* RIS -- Reset to inital state */
                                treset();
                                term.esc = 0;
@@ -1952,20 +2009,26 @@ tputc(char *c, int len) {
                                term.esc = 0;
                        }
                }
-       } else {
-               if(sel.bx != -1 && BETWEEN(term.c.y, sel.by, sel.ey))
-                       sel.bx = -1;
-               if(ascii >= '\020' || term.c.attr.mode & ATTR_GFX) {
-                       if(IS_SET(MODE_WRAP) && term.c.state & CURSOR_WRAPNEXT)
-                               tnewline(1); /* always go to first col */
-                       tsetchar(c);
-                       if(term.c.x+1 < term.col) {
-                               tmoveto(term.c.x+1, term.c.y);
-                       } else {
-                               term.c.state |= CURSOR_WRAPNEXT;
-                       }
-               }
+               /*
+                * All characters which forms part of a sequence are not
+                * printed
+                */
+               return;
        }
+       /*
+        * Display control codes only if we are in graphic mode
+        */
+       if(control && !(term.c.attr.mode & ATTR_GFX))
+               return;
+       if(sel.bx != -1 && BETWEEN(term.c.y, sel.by, sel.ey))
+               sel.bx = -1;
+       if(IS_SET(MODE_WRAP) && term.c.state & CURSOR_WRAPNEXT)
+               tnewline(1); /* always go to first col */
+       tsetchar(c, &term.c.attr, term.c.x, term.c.y);
+       if(term.c.x+1 < term.col)
+               tmoveto(term.c.x+1, term.c.y);
+       else
+               term.c.state |= CURSOR_WRAPNEXT;
 }
 
 int
@@ -2293,7 +2356,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
                 * Those ranges will not be brightened:
                 *      8 - 15 – bright system colors
                 *      196 - 231 – highest 256 color cube
-                *      252 - 255 – brightest colors in grescale
+                *      252 - 255 – brightest colors in greyscale
                 */
                font = &dc.bfont;
        }
@@ -2338,7 +2401,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
        }
        if(x + charlen >= term.col-1) {
                xclear(winx + width, (y == 0)? 0 : winy, xw.w,
-                       winy + xw.ch + (y == term.row-1)? xw.h : 0);
+                       (y == term.row-1)? xw.h : (winy + xw.ch));
        }
        if(y == 0)
                xclear(winx, 0, winx + width, BORDER);