t* st + patches and config
       
   URI git clone git://git.codevoid.de/st-sdk
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tst.c (57387B)
       ---
            1 /* See LICENSE for license details. */
            2 #include <ctype.h>
            3 #include <errno.h>
            4 #include <fcntl.h>
            5 #include <limits.h>
            6 #include <pwd.h>
            7 #include <stdarg.h>
            8 #include <stdio.h>
            9 #include <stdlib.h>
           10 #include <string.h>
           11 #include <signal.h>
           12 #include <sys/ioctl.h>
           13 #include <sys/select.h>
           14 #include <sys/types.h>
           15 #include <sys/wait.h>
           16 #include <termios.h>
           17 #include <unistd.h>
           18 #include <wchar.h>
           19 
           20 #include "st.h"
           21 #include "win.h"
           22 
           23 #if   defined(__linux)
           24  #include <pty.h>
           25 #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
           26  #include <util.h>
           27 #elif defined(__FreeBSD__) || defined(__DragonFly__)
           28  #include <libutil.h>
           29 #endif
           30 
           31 /* Arbitrary sizes */
           32 #define UTF_INVALID   0xFFFD
           33 #define UTF_SIZ       4
           34 #define ESC_BUF_SIZ   (128*UTF_SIZ)
           35 #define ESC_ARG_SIZ   16
           36 #define STR_BUF_SIZ   ESC_BUF_SIZ
           37 #define STR_ARG_SIZ   ESC_ARG_SIZ
           38 #define HISTSIZE      2000
           39 
           40 /* macros */
           41 #define IS_SET(flag)                ((term.mode & (flag)) != 0)
           42 #define ISCONTROLC0(c)                (BETWEEN(c, 0, 0x1f) || (c) == 0x7f)
           43 #define ISCONTROLC1(c)                (BETWEEN(c, 0x80, 0x9f))
           44 #define ISCONTROL(c)                (ISCONTROLC0(c) || ISCONTROLC1(c))
           45 #define ISDELIM(u)                (u && wcschr(worddelimiters, u))
           46 #define TLINE(y)                ((y) < term.scr ? term.hist[((y) + term.histi - \
           47                                 term.scr + HISTSIZE + 1) % HISTSIZE] : \
           48                                 term.line[(y) - term.scr])
           49 
           50 enum term_mode {
           51         MODE_WRAP        = 1 << 0,
           52         MODE_INSERT      = 1 << 1,
           53         MODE_ALTSCREEN   = 1 << 2,
           54         MODE_CRLF        = 1 << 3,
           55         MODE_ECHO        = 1 << 4,
           56         MODE_PRINT       = 1 << 5,
           57         MODE_UTF8        = 1 << 6,
           58 };
           59 
           60 enum cursor_movement {
           61         CURSOR_SAVE,
           62         CURSOR_LOAD
           63 };
           64 
           65 enum cursor_state {
           66         CURSOR_DEFAULT  = 0,
           67         CURSOR_WRAPNEXT = 1,
           68         CURSOR_ORIGIN   = 2
           69 };
           70 
           71 enum charset {
           72         CS_GRAPHIC0,
           73         CS_GRAPHIC1,
           74         CS_UK,
           75         CS_USA,
           76         CS_MULTI,
           77         CS_GER,
           78         CS_FIN
           79 };
           80 
           81 enum escape_state {
           82         ESC_START      = 1,
           83         ESC_CSI        = 2,
           84         ESC_STR        = 4,  /* DCS, OSC, PM, APC */
           85         ESC_ALTCHARSET = 8,
           86         ESC_STR_END    = 16, /* a final string was encountered */
           87         ESC_TEST       = 32, /* Enter in test mode */
           88         ESC_UTF8       = 64,
           89 };
           90 
           91 typedef struct {
           92         Glyph attr; /* current char attributes */
           93         int x;
           94         int y;
           95         char state;
           96 } TCursor;
           97 
           98 typedef struct {
           99         int mode;
          100         int type;
          101         int snap;
          102         /*
          103          * Selection variables:
          104          * nb – normalized coordinates of the beginning of the selection
          105          * ne – normalized coordinates of the end of the selection
          106          * ob – original coordinates of the beginning of the selection
          107          * oe – original coordinates of the end of the selection
          108          */
          109         struct {
          110                 int x, y;
          111         } nb, ne, ob, oe;
          112 
          113         int alt;
          114 } Selection;
          115 
          116 /* Internal representation of the screen */
          117 typedef struct {
          118         int row;      /* nb row */
          119         int col;      /* nb col */
          120         Line *line;   /* screen */
          121         Line *alt;    /* alternate screen */
          122         Line hist[HISTSIZE]; /* history buffer */
          123         int histi;    /* history index */
          124         int scr;      /* scroll back */
          125         int *dirty;   /* dirtyness of lines */
          126         TCursor c;    /* cursor */
          127         int ocx;      /* old cursor col */
          128         int ocy;      /* old cursor row */
          129         int top;      /* top    scroll limit */
          130         int bot;      /* bottom scroll limit */
          131         int mode;     /* terminal mode flags */
          132         int esc;      /* escape state flags */
          133         char trantbl[4]; /* charset table translation */
          134         int charset;  /* current charset */
          135         int icharset; /* selected charset for sequence */
          136         int *tabs;
          137         Rune lastc;   /* last printed char outside of sequence, 0 if control */
          138 } Term;
          139 
          140 /* CSI Escape sequence structs */
          141 /* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
          142 typedef struct {
          143         char buf[ESC_BUF_SIZ]; /* raw string */
          144         size_t len;            /* raw string length */
          145         char priv;
          146         int arg[ESC_ARG_SIZ];
          147         int narg;              /* nb of args */
          148         char mode[2];
          149 } CSIEscape;
          150 
          151 /* STR Escape sequence structs */
          152 /* ESC type [[ [<priv>] <arg> [;]] <mode>] ESC '\' */
          153 typedef struct {
          154         char type;             /* ESC type ... */
          155         char *buf;             /* allocated raw string */
          156         size_t siz;            /* allocation size */
          157         size_t len;            /* raw string length */
          158         char *args[STR_ARG_SIZ];
          159         int narg;              /* nb of args */
          160 } STREscape;
          161 
          162 static void execsh(char *, char **);
          163 static void stty(char **);
          164 static void sigchld(int);
          165 static void ttywriteraw(const char *, size_t);
          166 
          167 static void csidump(void);
          168 static void csihandle(void);
          169 static void csiparse(void);
          170 static void csireset(void);
          171 static int eschandle(uchar);
          172 static void strdump(void);
          173 static void strhandle(void);
          174 static void strparse(void);
          175 static void strreset(void);
          176 
          177 static void tprinter(char *, size_t);
          178 static void tdumpsel(void);
          179 static void tdumpline(int);
          180 static void tdump(void);
          181 static void tclearregion(int, int, int, int);
          182 static void tcursor(int);
          183 static void tdeletechar(int);
          184 static void tdeleteline(int);
          185 static void tinsertblank(int);
          186 static void tinsertblankline(int);
          187 static int tlinelen(int);
          188 static void tmoveto(int, int);
          189 static void tmoveato(int, int);
          190 static void tnewline(int);
          191 static void tputtab(int);
          192 static void tputc(Rune);
          193 static void treset(void);
          194 static void tscrollup(int, int, int);
          195 static void tscrolldown(int, int, int);
          196 static void tsetattr(int *, int);
          197 static void tsetchar(Rune, Glyph *, int, int);
          198 static void tsetdirt(int, int);
          199 static void tsetscroll(int, int);
          200 static void tswapscreen(void);
          201 static void tsetmode(int, int, int *, int);
          202 static int twrite(const char *, int, int);
          203 static void tfulldirt(void);
          204 static void tcontrolcode(uchar );
          205 static void tdectest(char );
          206 static void tdefutf8(char);
          207 static int32_t tdefcolor(int *, int *, int);
          208 static void tdeftran(char);
          209 static void tstrsequence(uchar);
          210 
          211 static void drawregion(int, int, int, int);
          212 
          213 static void selnormalize(void);
          214 static void selscroll(int, int);
          215 static void selsnap(int *, int *, int);
          216 
          217 static size_t utf8decode(const char *, Rune *, size_t);
          218 static Rune utf8decodebyte(char, size_t *);
          219 static char utf8encodebyte(Rune, size_t);
          220 static size_t utf8validate(Rune *, size_t);
          221 
          222 static char *base64dec(const char *);
          223 static char base64dec_getc(const char **);
          224 
          225 static ssize_t xwrite(int, const char *, size_t);
          226 
          227 /* Globals */
          228 static Term term;
          229 static Selection sel;
          230 static CSIEscape csiescseq;
          231 static STREscape strescseq;
          232 static int iofd = 1;
          233 static int cmdfd;
          234 static pid_t pid;
          235 
          236 static uchar utfbyte[UTF_SIZ + 1] = {0x80,    0, 0xC0, 0xE0, 0xF0};
          237 static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
          238 static Rune utfmin[UTF_SIZ + 1] = {       0,    0,  0x80,  0x800,  0x10000};
          239 static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
          240 
          241 ssize_t
          242 xwrite(int fd, const char *s, size_t len)
          243 {
          244         size_t aux = len;
          245         ssize_t r;
          246 
          247         while (len > 0) {
          248                 r = write(fd, s, len);
          249                 if (r < 0)
          250                         return r;
          251                 len -= r;
          252                 s += r;
          253         }
          254 
          255         return aux;
          256 }
          257 
          258 void *
          259 xmalloc(size_t len)
          260 {
          261         void *p;
          262 
          263         if (!(p = malloc(len)))
          264                 die("malloc: %s\n", strerror(errno));
          265 
          266         return p;
          267 }
          268 
          269 void *
          270 xrealloc(void *p, size_t len)
          271 {
          272         if ((p = realloc(p, len)) == NULL)
          273                 die("realloc: %s\n", strerror(errno));
          274 
          275         return p;
          276 }
          277 
          278 char *
          279 xstrdup(char *s)
          280 {
          281         if ((s = strdup(s)) == NULL)
          282                 die("strdup: %s\n", strerror(errno));
          283 
          284         return s;
          285 }
          286 
          287 size_t
          288 utf8decode(const char *c, Rune *u, size_t clen)
          289 {
          290         size_t i, j, len, type;
          291         Rune udecoded;
          292 
          293         *u = UTF_INVALID;
          294         if (!clen)
          295                 return 0;
          296         udecoded = utf8decodebyte(c[0], &len);
          297         if (!BETWEEN(len, 1, UTF_SIZ))
          298                 return 1;
          299         for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
          300                 udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
          301                 if (type != 0)
          302                         return j;
          303         }
          304         if (j < len)
          305                 return 0;
          306         *u = udecoded;
          307         utf8validate(u, len);
          308 
          309         return len;
          310 }
          311 
          312 Rune
          313 utf8decodebyte(char c, size_t *i)
          314 {
          315         for (*i = 0; *i < LEN(utfmask); ++(*i))
          316                 if (((uchar)c & utfmask[*i]) == utfbyte[*i])
          317                         return (uchar)c & ~utfmask[*i];
          318 
          319         return 0;
          320 }
          321 
          322 size_t
          323 utf8encode(Rune u, char *c)
          324 {
          325         size_t len, i;
          326 
          327         len = utf8validate(&u, 0);
          328         if (len > UTF_SIZ)
          329                 return 0;
          330 
          331         for (i = len - 1; i != 0; --i) {
          332                 c[i] = utf8encodebyte(u, 0);
          333                 u >>= 6;
          334         }
          335         c[0] = utf8encodebyte(u, len);
          336 
          337         return len;
          338 }
          339 
          340 char
          341 utf8encodebyte(Rune u, size_t i)
          342 {
          343         return utfbyte[i] | (u & ~utfmask[i]);
          344 }
          345 
          346 size_t
          347 utf8validate(Rune *u, size_t i)
          348 {
          349         if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
          350                 *u = UTF_INVALID;
          351         for (i = 1; *u > utfmax[i]; ++i)
          352                 ;
          353 
          354         return i;
          355 }
          356 
          357 static const char base64_digits[] = {
          358         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          359         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0,
          360         63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1,
          361         2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
          362         22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34,
          363         35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0,
          364         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          365         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          366         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          367         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          368         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          369         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
          370 };
          371 
          372 char
          373 base64dec_getc(const char **src)
          374 {
          375         while (**src && !isprint(**src))
          376                 (*src)++;
          377         return **src ? *((*src)++) : '=';  /* emulate padding if string ends */
          378 }
          379 
          380 char *
          381 base64dec(const char *src)
          382 {
          383         size_t in_len = strlen(src);
          384         char *result, *dst;
          385 
          386         if (in_len % 4)
          387                 in_len += 4 - (in_len % 4);
          388         result = dst = xmalloc(in_len / 4 * 3 + 1);
          389         while (*src) {
          390                 int a = base64_digits[(unsigned char) base64dec_getc(&src)];
          391                 int b = base64_digits[(unsigned char) base64dec_getc(&src)];
          392                 int c = base64_digits[(unsigned char) base64dec_getc(&src)];
          393                 int d = base64_digits[(unsigned char) base64dec_getc(&src)];
          394 
          395                 /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */
          396                 if (a == -1 || b == -1)
          397                         break;
          398 
          399                 *dst++ = (a << 2) | ((b & 0x30) >> 4);
          400                 if (c == -1)
          401                         break;
          402                 *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2);
          403                 if (d == -1)
          404                         break;
          405                 *dst++ = ((c & 0x03) << 6) | d;
          406         }
          407         *dst = '\0';
          408         return result;
          409 }
          410 
          411 void
          412 selinit(void)
          413 {
          414         sel.mode = SEL_IDLE;
          415         sel.snap = 0;
          416         sel.ob.x = -1;
          417 }
          418 
          419 int
          420 tlinelen(int y)
          421 {
          422         int i = term.col;
          423 
          424         if (TLINE(y)[i - 1].mode & ATTR_WRAP)
          425                 return i;
          426 
          427         while (i > 0 && TLINE(y)[i - 1].u == ' ')
          428                 --i;
          429 
          430         return i;
          431 }
          432 
          433 void
          434 selstart(int col, int row, int snap)
          435 {
          436         selclear();
          437         sel.mode = SEL_EMPTY;
          438         sel.type = SEL_REGULAR;
          439         sel.alt = IS_SET(MODE_ALTSCREEN);
          440         sel.snap = snap;
          441         sel.oe.x = sel.ob.x = col;
          442         sel.oe.y = sel.ob.y = row;
          443         selnormalize();
          444 
          445         if (sel.snap != 0)
          446                 sel.mode = SEL_READY;
          447         tsetdirt(sel.nb.y, sel.ne.y);
          448 }
          449 
          450 void
          451 selextend(int col, int row, int type, int done)
          452 {
          453         int oldey, oldex, oldsby, oldsey, oldtype;
          454 
          455         if (sel.mode == SEL_IDLE)
          456                 return;
          457         if (done && sel.mode == SEL_EMPTY) {
          458                 selclear();
          459                 return;
          460         }
          461 
          462         oldey = sel.oe.y;
          463         oldex = sel.oe.x;
          464         oldsby = sel.nb.y;
          465         oldsey = sel.ne.y;
          466         oldtype = sel.type;
          467 
          468         sel.oe.x = col;
          469         sel.oe.y = row;
          470         selnormalize();
          471         sel.type = type;
          472 
          473         if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY)
          474                 tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
          475 
          476         sel.mode = done ? SEL_IDLE : SEL_READY;
          477 }
          478 
          479 void
          480 selnormalize(void)
          481 {
          482         int i;
          483 
          484         if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
          485                 sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
          486                 sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
          487         } else {
          488                 sel.nb.x = MIN(sel.ob.x, sel.oe.x);
          489                 sel.ne.x = MAX(sel.ob.x, sel.oe.x);
          490         }
          491         sel.nb.y = MIN(sel.ob.y, sel.oe.y);
          492         sel.ne.y = MAX(sel.ob.y, sel.oe.y);
          493 
          494         selsnap(&sel.nb.x, &sel.nb.y, -1);
          495         selsnap(&sel.ne.x, &sel.ne.y, +1);
          496 
          497         /* expand selection over line breaks */
          498         if (sel.type == SEL_RECTANGULAR)
          499                 return;
          500         i = tlinelen(sel.nb.y);
          501         if (i < sel.nb.x)
          502                 sel.nb.x = i;
          503         if (tlinelen(sel.ne.y) <= sel.ne.x)
          504                 sel.ne.x = term.col - 1;
          505 }
          506 
          507 int
          508 selected(int x, int y)
          509 {
          510         if (sel.mode == SEL_EMPTY || sel.ob.x == -1 ||
          511                         sel.alt != IS_SET(MODE_ALTSCREEN))
          512                 return 0;
          513 
          514         if (sel.type == SEL_RECTANGULAR)
          515                 return BETWEEN(y, sel.nb.y, sel.ne.y)
          516                     && BETWEEN(x, sel.nb.x, sel.ne.x);
          517 
          518         return BETWEEN(y, sel.nb.y, sel.ne.y)
          519             && (y != sel.nb.y || x >= sel.nb.x)
          520             && (y != sel.ne.y || x <= sel.ne.x);
          521 }
          522 
          523 void
          524 selsnap(int *x, int *y, int direction)
          525 {
          526         int newx, newy, xt, yt;
          527         int delim, prevdelim;
          528         Glyph *gp, *prevgp;
          529 
          530         switch (sel.snap) {
          531         case SNAP_WORD:
          532                 /*
          533                  * Snap around if the word wraps around at the end or
          534                  * beginning of a line.
          535                  */
          536                 prevgp = &TLINE(*y)[*x];
          537                 prevdelim = ISDELIM(prevgp->u);
          538                 for (;;) {
          539                         newx = *x + direction;
          540                         newy = *y;
          541                         if (!BETWEEN(newx, 0, term.col - 1)) {
          542                                 newy += direction;
          543                                 newx = (newx + term.col) % term.col;
          544                                 if (!BETWEEN(newy, 0, term.row - 1))
          545                                         break;
          546 
          547                                 if (direction > 0)
          548                                         yt = *y, xt = *x;
          549                                 else
          550                                         yt = newy, xt = newx;
          551                                 if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
          552                                         break;
          553                         }
          554 
          555                         if (newx >= tlinelen(newy))
          556                                 break;
          557 
          558                         gp = &TLINE(newy)[newx];
          559                         delim = ISDELIM(gp->u);
          560                         if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
          561                                         || (delim && gp->u != prevgp->u)))
          562                                 break;
          563 
          564                         *x = newx;
          565                         *y = newy;
          566                         prevgp = gp;
          567                         prevdelim = delim;
          568                 }
          569                 break;
          570         case SNAP_LINE:
          571                 /*
          572                  * Snap around if the the previous line or the current one
          573                  * has set ATTR_WRAP at its end. Then the whole next or
          574                  * previous line will be selected.
          575                  */
          576                 *x = (direction < 0) ? 0 : term.col - 1;
          577                 if (direction < 0) {
          578                         for (; *y > 0; *y += direction) {
          579                                 if (!(TLINE(*y-1)[term.col-1].mode
          580                                                 & ATTR_WRAP)) {
          581                                         break;
          582                                 }
          583                         }
          584                 } else if (direction > 0) {
          585                         for (; *y < term.row-1; *y += direction) {
          586                                 if (!(TLINE(*y)[term.col-1].mode
          587                                                 & ATTR_WRAP)) {
          588                                         break;
          589                                 }
          590                         }
          591                 }
          592                 break;
          593         }
          594 }
          595 
          596 char *
          597 getsel(void)
          598 {
          599         char *str, *ptr;
          600         int y, bufsize, lastx, linelen;
          601         Glyph *gp, *last;
          602 
          603         if (sel.ob.x == -1)
          604                 return NULL;
          605 
          606         bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ;
          607         ptr = str = xmalloc(bufsize);
          608 
          609         /* append every set & selected glyph to the selection */
          610         for (y = sel.nb.y; y <= sel.ne.y; y++) {
          611                 if ((linelen = tlinelen(y)) == 0) {
          612                         *ptr++ = '\n';
          613                         continue;
          614                 }
          615 
          616                 if (sel.type == SEL_RECTANGULAR) {
          617                         gp = &TLINE(y)[sel.nb.x];
          618                         lastx = sel.ne.x;
          619                 } else {
          620                         gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];
          621                         lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
          622                 }
          623                 last = &TLINE(y)[MIN(lastx, linelen-1)];
          624                 while (last >= gp && last->u == ' ')
          625                         --last;
          626 
          627                 for ( ; gp <= last; ++gp) {
          628                         if (gp->mode & ATTR_WDUMMY)
          629                                 continue;
          630 
          631                         ptr += utf8encode(gp->u, ptr);
          632                 }
          633 
          634                 /*
          635                  * Copy and pasting of line endings is inconsistent
          636                  * in the inconsistent terminal and GUI world.
          637                  * The best solution seems like to produce '\n' when
          638                  * something is copied from st and convert '\n' to
          639                  * '\r', when something to be pasted is received by
          640                  * st.
          641                  * FIXME: Fix the computer world.
          642                  */
          643                 if ((y < sel.ne.y || lastx >= linelen) &&
          644                     (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
          645                         *ptr++ = '\n';
          646         }
          647         *ptr = 0;
          648         return str;
          649 }
          650 
          651 void
          652 selclear(void)
          653 {
          654         if (sel.ob.x == -1)
          655                 return;
          656         sel.mode = SEL_IDLE;
          657         sel.ob.x = -1;
          658         tsetdirt(sel.nb.y, sel.ne.y);
          659 }
          660 
          661 void
          662 die(const char *errstr, ...)
          663 {
          664         va_list ap;
          665 
          666         va_start(ap, errstr);
          667         vfprintf(stderr, errstr, ap);
          668         va_end(ap);
          669         exit(1);
          670 }
          671 
          672 void
          673 execsh(char *cmd, char **args)
          674 {
          675         char *sh, *prog, *arg;
          676         const struct passwd *pw;
          677 
          678         errno = 0;
          679         if ((pw = getpwuid(getuid())) == NULL) {
          680                 if (errno)
          681                         die("getpwuid: %s\n", strerror(errno));
          682                 else
          683                         die("who are you?\n");
          684         }
          685 
          686         if ((sh = getenv("SHELL")) == NULL)
          687                 sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd;
          688 
          689         if (args) {
          690                 prog = args[0];
          691                 arg = NULL;
          692         } else if (scroll) {
          693                 prog = scroll;
          694                 arg = utmp ? utmp : sh;
          695         } else if (utmp) {
          696                 prog = utmp;
          697                 arg = NULL;
          698         } else {
          699                 prog = sh;
          700                 arg = NULL;
          701         }
          702         DEFAULT(args, ((char *[]) {prog, arg, NULL}));
          703 
          704         unsetenv("COLUMNS");
          705         unsetenv("LINES");
          706         unsetenv("TERMCAP");
          707         setenv("LOGNAME", pw->pw_name, 1);
          708         setenv("USER", pw->pw_name, 1);
          709         setenv("SHELL", sh, 1);
          710         setenv("HOME", pw->pw_dir, 1);
          711         setenv("TERM", termname, 1);
          712 
          713         signal(SIGCHLD, SIG_DFL);
          714         signal(SIGHUP, SIG_DFL);
          715         signal(SIGINT, SIG_DFL);
          716         signal(SIGQUIT, SIG_DFL);
          717         signal(SIGTERM, SIG_DFL);
          718         signal(SIGALRM, SIG_DFL);
          719 
          720         execvp(prog, args);
          721         _exit(1);
          722 }
          723 
          724 void
          725 sigchld(int a)
          726 {
          727         int stat;
          728         pid_t p;
          729 
          730         if ((p = waitpid(pid, &stat, WNOHANG)) < 0)
          731                 die("waiting for pid %hd failed: %s\n", pid, strerror(errno));
          732 
          733         if (pid != p)
          734                 return;
          735 
          736         if (WIFEXITED(stat) && WEXITSTATUS(stat))
          737                 die("child exited with status %d\n", WEXITSTATUS(stat));
          738         else if (WIFSIGNALED(stat))
          739                 die("child terminated due to signal %d\n", WTERMSIG(stat));
          740         _exit(0);
          741 }
          742 
          743 void
          744 stty(char **args)
          745 {
          746         char cmd[_POSIX_ARG_MAX], **p, *q, *s;
          747         size_t n, siz;
          748 
          749         if ((n = strlen(stty_args)) > sizeof(cmd)-1)
          750                 die("incorrect stty parameters\n");
          751         memcpy(cmd, stty_args, n);
          752         q = cmd + n;
          753         siz = sizeof(cmd) - n;
          754         for (p = args; p && (s = *p); ++p) {
          755                 if ((n = strlen(s)) > siz-1)
          756                         die("stty parameter length too long\n");
          757                 *q++ = ' ';
          758                 memcpy(q, s, n);
          759                 q += n;
          760                 siz -= n + 1;
          761         }
          762         *q = '\0';
          763         if (system(cmd) != 0)
          764                 perror("Couldn't call stty");
          765 }
          766 
          767 int
          768 ttynew(char *line, char *cmd, char *out, char **args)
          769 {
          770         int m, s;
          771 
          772         if (out) {
          773                 term.mode |= MODE_PRINT;
          774                 iofd = (!strcmp(out, "-")) ?
          775                           1 : open(out, O_WRONLY | O_CREAT, 0666);
          776                 if (iofd < 0) {
          777                         fprintf(stderr, "Error opening %s:%s\n",
          778                                 out, strerror(errno));
          779                 }
          780         }
          781 
          782         if (line) {
          783                 if ((cmdfd = open(line, O_RDWR)) < 0)
          784                         die("open line '%s' failed: %s\n",
          785                             line, strerror(errno));
          786                 dup2(cmdfd, 0);
          787                 stty(args);
          788                 return cmdfd;
          789         }
          790 
          791         /* seems to work fine on linux, openbsd and freebsd */
          792         if (openpty(&m, &s, NULL, NULL, NULL) < 0)
          793                 die("openpty failed: %s\n", strerror(errno));
          794 
          795         switch (pid = fork()) {
          796         case -1:
          797                 die("fork failed: %s\n", strerror(errno));
          798                 break;
          799         case 0:
          800                 close(iofd);
          801                 setsid(); /* create a new process group */
          802                 dup2(s, 0);
          803                 dup2(s, 1);
          804                 dup2(s, 2);
          805                 if (ioctl(s, TIOCSCTTY, NULL) < 0)
          806                         die("ioctl TIOCSCTTY failed: %s\n", strerror(errno));
          807                 close(s);
          808                 close(m);
          809 #ifdef __OpenBSD__
          810                 if (pledge("stdio getpw proc exec", NULL) == -1)
          811                         die("pledge\n");
          812 #endif
          813                 execsh(cmd, args);
          814                 break;
          815         default:
          816 #ifdef __OpenBSD__
          817                 if (pledge("stdio rpath tty proc", NULL) == -1)
          818                         die("pledge\n");
          819 #endif
          820                 close(s);
          821                 cmdfd = m;
          822                 signal(SIGCHLD, sigchld);
          823                 break;
          824         }
          825         return cmdfd;
          826 }
          827 
          828 size_t
          829 ttyread(void)
          830 {
          831         static char buf[BUFSIZ];
          832         static int buflen = 0;
          833         int ret, written;
          834 
          835         /* append read bytes to unprocessed bytes */
          836         ret = read(cmdfd, buf+buflen, LEN(buf)-buflen);
          837 
          838         switch (ret) {
          839         case 0:
          840                 exit(0);
          841         case -1:
          842                 die("couldn't read from shell: %s\n", strerror(errno));
          843         default:
          844                 buflen += ret;
          845                 written = twrite(buf, buflen, 0);
          846                 buflen -= written;
          847                 /* keep any incomplete UTF-8 byte sequence for the next call */
          848                 if (buflen > 0)
          849                         memmove(buf, buf + written, buflen);
          850                 return ret;
          851         }
          852 }
          853 
          854 void
          855 ttywrite(const char *s, size_t n, int may_echo)
          856 {
          857         const char *next;
          858         Arg arg = (Arg) { .i = term.scr };
          859 
          860         kscrolldown(&arg);
          861 
          862         if (may_echo && IS_SET(MODE_ECHO))
          863                 twrite(s, n, 1);
          864 
          865         if (!IS_SET(MODE_CRLF)) {
          866                 ttywriteraw(s, n);
          867                 return;
          868         }
          869 
          870         /* This is similar to how the kernel handles ONLCR for ttys */
          871         while (n > 0) {
          872                 if (*s == '\r') {
          873                         next = s + 1;
          874                         ttywriteraw("\r\n", 2);
          875                 } else {
          876                         next = memchr(s, '\r', n);
          877                         DEFAULT(next, s + n);
          878                         ttywriteraw(s, next - s);
          879                 }
          880                 n -= next - s;
          881                 s = next;
          882         }
          883 }
          884 
          885 void
          886 ttywriteraw(const char *s, size_t n)
          887 {
          888         fd_set wfd, rfd;
          889         ssize_t r;
          890         size_t lim = 256;
          891 
          892         /*
          893          * Remember that we are using a pty, which might be a modem line.
          894          * Writing too much will clog the line. That's why we are doing this
          895          * dance.
          896          * FIXME: Migrate the world to Plan 9.
          897          */
          898         while (n > 0) {
          899                 FD_ZERO(&wfd);
          900                 FD_ZERO(&rfd);
          901                 FD_SET(cmdfd, &wfd);
          902                 FD_SET(cmdfd, &rfd);
          903 
          904                 /* Check if we can write. */
          905                 if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) {
          906                         if (errno == EINTR)
          907                                 continue;
          908