💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Menkar › files › 03cb5836054dd9904c0fc46a559… captured on 2023-04-19 at 23:16:37. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-03-20)

➡️ Next capture (2023-09-08)

-=-=-=-=-=-=-

0 /* See LICENSE for license details. */

1 #include <ctype.h>

2 #include <errno.h>

3 #include <fcntl.h>

4 #include <limits.h>

5 #include <pwd.h>

6 #include <stdarg.h>

7 #include <stdio.h>

8 #include <stdlib.h>

9 #include <string.h>

10 #include <signal.h>

11 #include <sys/ioctl.h>

12 #include <sys/select.h>

13 #include <sys/types.h>

14 #include <sys/wait.h>

15 #include <termios.h>

16 #include <unistd.h>

17 #include <wchar.h>

18

19 #include "st.h"

20 #include "win.h"

21

22 #if defined(__linux)

23 #include <pty.h>

24 #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)

25 #include <util.h>

26 #elif defined(__FreeBSD__) || defined(__DragonFly__)

27 #include <libutil.h>

28 #endif

29

30 /* Arbitrary sizes */

31 #define UTF_INVALID 0xFFFD

32 #define UTF_SIZ 4

33 #define ESC_BUF_SIZ (128*UTF_SIZ)

34 #define ESC_ARG_SIZ 16

35 #define STR_BUF_SIZ ESC_BUF_SIZ

36 #define STR_ARG_SIZ ESC_ARG_SIZ

37 #define HISTSIZE 8000

38

39 /* macros */

40 #define IS_SET(flag) ((term.mode & (flag)) != 0)

41 #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f)

42 #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))

43 #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))

44 #define ISDELIM(u) (u && wcschr(worddelimiters, u))

45 #define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \

46 term.scr + HISTSIZE + 1) % HISTSIZE] : \

47 term.line[(y) - term.scr])

48

49 enum term_mode {

50 MODE_WRAP = 1 << 0,

51 MODE_INSERT = 1 << 1,

52 MODE_ALTSCREEN = 1 << 2,

53 MODE_CRLF = 1 << 3,

54 MODE_ECHO = 1 << 4,

55 MODE_PRINT = 1 << 5,

56 MODE_UTF8 = 1 << 6,

57 };

58

59 enum cursor_movement {

60 CURSOR_SAVE,

61 CURSOR_LOAD

62 };

63

64 enum cursor_state {

65 CURSOR_DEFAULT = 0,

66 CURSOR_WRAPNEXT = 1,

67 CURSOR_ORIGIN = 2

68 };

69

70 enum charset {

71 CS_GRAPHIC0,

72 CS_GRAPHIC1,

73 CS_UK,

74 CS_USA,

75 CS_MULTI,

76 CS_GER,

77 CS_FIN

78 };

79

80 enum escape_state {

81 ESC_START = 1,

82 ESC_CSI = 2,

83 ESC_STR = 4, /* DCS, OSC, PM, APC */

84 ESC_ALTCHARSET = 8,

85 ESC_STR_END = 16, /* a final string was encountered */

86 ESC_TEST = 32, /* Enter in test mode */

87 ESC_UTF8 = 64,

88 };

89

90 typedef struct {

91 Glyph attr; /* current char attributes */

92 int x;

93 int y;

94 char state;

95 } TCursor;

96

97 typedef struct {

98 int mode;

99 int type;

100 int snap;

101 /*

102 * Selection variables:

103 * nb – normalized coordinates of the beginning of the selection

104 * ne – normalized coordinates of the end of the selection

105 * ob – original coordinates of the beginning of the selection

106 * oe – original coordinates of the end of the selection

107 */

108 struct {

109 int x, y;

110 } nb, ne, ob, oe;

111

112 int alt;

113 } Selection;

114

115 /* Internal representation of the screen */

116 typedef struct {

117 int row; /* nb row */

118 int col; /* nb col */

119 Line *line; /* screen */

120 Line *alt; /* alternate screen */

121 Line hist[HISTSIZE]; /* history buffer */

122 int histi; /* history index */

123 int scr; /* scroll back */

124 int *dirty; /* dirtyness of lines */

125 TCursor c; /* cursor */

126 int ocx; /* old cursor col */

127 int ocy; /* old cursor row */

128 int top; /* top scroll limit */

129 int bot; /* bottom scroll limit */

130 int mode; /* terminal mode flags */

131 int esc; /* escape state flags */

132 char trantbl[4]; /* charset table translation */

133 int charset; /* current charset */

134 int icharset; /* selected charset for sequence */

135 int *tabs;

136 Rune lastc; /* last printed char outside of sequence, 0 if control */

137 } Term;

138

139 /* CSI Escape sequence structs */

140 /* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */

141 typedef struct {

142 char buf[ESC_BUF_SIZ]; /* raw string */

143 size_t len; /* raw string length */

144 char priv;

145 int arg[ESC_ARG_SIZ];

146 int narg; /* nb of args */

147 char mode[2];

148 } CSIEscape;

149

150 /* STR Escape sequence structs */

151 /* ESC type [[ [<priv>] <arg> [;]] <mode>] ESC '\' */

152 typedef struct {

153 char type; /* ESC type ... */

154 char *buf; /* allocated raw string */

155 size_t siz; /* allocation size */

156 size_t len; /* raw string length */

157 char *args[STR_ARG_SIZ];

158 int narg; /* nb of args */

159 } STREscape;

160

161 static void execsh(char *, char **);

162 static void stty(char **);

163 static void sigchld(int);

164 static void ttywriteraw(const char *, size_t);

165

166 #ifdef DEBUG

167 static void csidump(void);

168 #endif

169 static void csihandle(void);

170 static void csiparse(void);

171 static void csireset(void);

172 static int eschandle(uchar);

173 #ifdef DEBUG

174 static void strdump(void);

175 #endif

176 static void strhandle(void);

177 static void strparse(void);

178 static void strreset(void);

179

180 static void tprinter(char *, size_t);

181 static void tdumpsel(void);

182 static void tdumpline(int);

183 static void tdump(void);

184 static void tclearregion(int, int, int, int);

185 static void tcursor(int);

186 static void tdeletechar(int);

187 static void tdeleteline(int);

188 static void tinsertblank(int);

189 static void tinsertblankline(int);

190 static int tlinelen(int);

191 static void tmoveto(int, int);

192 static void tmoveato(int, int);

193 static void tnewline(int);

194 static void tputtab(int);

195 static void tputc(Rune);

196 static void treset(void);

197 static void tscrollup(int, int, int);

198 static void tscrolldown(int, int, int);

199 static void tsetattr(const int *, int);

200 static void tsetchar(Rune, const Glyph *, int, int);

201 static void tsetdirt(int, int);

202 static void tsetscroll(int, int);

203 static void tswapscreen(void);

204 static void tsetmode(int, int, const int *, int);

205 static int twrite(const char *, int, int);

206 static void tfulldirt(void);

207 static void tcontrolcode(uchar );

208 static void tdectest(char );

209 static void tdefutf8(char);

210 static int32_t tdefcolor(const int *, int *, int);

211 static void tdeftran(char);

212 static void tstrsequence(uchar);

213

214 static void drawregion(int, int, int, int);

215

216 static void selnormalize(void);

217 static void selscroll(int, int);

218 static void selsnap(int *, int *, int);

219

220 static size_t utf8decode(const char *, Rune *, size_t);

221 static Rune utf8decodebyte(char, size_t *);

222 static char utf8encodebyte(Rune, size_t);

223 static size_t utf8validate(Rune *, size_t);

224

225 static char *base64dec(const char *);

226 static char base64dec_getc(const char **);

227

228 static ssize_t xwrite(int, const char *, size_t);

229

230 /* Globals */

231 static Term term;

232 static Selection sel;

233 static CSIEscape csiescseq;

234 static STREscape strescseq;

235 static int iofd = 1;

236 static int cmdfd;

237 static pid_t pid;

238

239 static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};

240 static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};

241 static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};

242 static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};

243

244 ssize_t

245 xwrite(int fd, const char *s, size_t len)

246 {

247 size_t aux = len;

248 ssize_t r;

249

250 while (len > 0) {

251 r = write(fd, s, len);

252 if (r < 0)

253 return r;

254 len -= r;

255 s += r;

256 }

257

258 return aux;

259 }

260

261 void *

262 xmalloc(size_t len)

263 {

264 void *p;

265

266 if (!(p = malloc(len)))

267 die("malloc: %s\n", strerror(errno));

268

269 return p;

270 }

271

272 void *

273 xrealloc(void *p, size_t len)

274 {

275 if ((p = realloc(p, len)) == NULL)

276 die("realloc: %s\n", strerror(errno));

277

278 return p;

279 }

280

281 char *

282 xstrdup(const char *s)

283 {

284 char *p;

285

286 if ((p = strdup(s)) == NULL)

287 die("strdup: %s\n", strerror(errno));

288

289 return p;

290 }

291

292 size_t

293 utf8decode(const char *c, Rune *u, size_t clen)

294 {

295 size_t i, j, len, type;

296 Rune udecoded;

297

298 *u = UTF_INVALID;

299 if (!clen)

300 return 0;

301 udecoded = utf8decodebyte(c[0], &len);

302 if (!BETWEEN(len, 1, UTF_SIZ))

303 return 1;

304 for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {

305 udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);

306 if (type != 0)

307 return j;

308 }

309 if (j < len)

310 return 0;

311 *u = udecoded;

312 utf8validate(u, len);

313

314 return len;

315 }

316

317 Rune

318 utf8decodebyte(char c, size_t *i)

319 {

320 for (*i = 0; *i < LEN(utfmask); ++(*i))

321 if (((uchar)c & utfmask[*i]) == utfbyte[*i])

322 return (uchar)c & ~utfmask[*i];

323

324 return 0;

325 }

326

327 size_t

328 utf8encode(Rune u, char *c)

329 {

330 size_t len, i;

331

332 len = utf8validate(&u, 0);

333 if (len > UTF_SIZ)

334 return 0;

335

336 for (i = len - 1; i != 0; --i) {

337 c[i] = utf8encodebyte(u, 0);

338 u >>= 6;

339 }

340 c[0] = utf8encodebyte(u, len);

341

342 return len;

343 }

344

345 char

346 utf8encodebyte(Rune u, size_t i)

347 {

348 return utfbyte[i] | (u & ~utfmask[i]);

349 }

350

351 size_t

352 utf8validate(Rune *u, size_t i)

353 {

354 if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))

355 *u = UTF_INVALID;

356 for (i = 1; *u > utfmax[i]; ++i)

357 ;

358

359 return i;

360 }

361

362 static const char base64_digits[] = {

363 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

364 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0,

365 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1,

366 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,

367 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34,

368 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0,

369 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

370 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

371 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

372 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

373 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

374 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0

375 };

376

377 char

378 base64dec_getc(const char **src)

379 {

380 while (**src && !isprint(**src))

381 (*src)++;

382 return **src ? *((*src)++) : '='; /* emulate padding if string ends */

383 }

384

385 char *

386 base64dec(const char *src)

387 {

388 size_t in_len = strlen(src);

389 char *result, *dst;

390

391 if (in_len % 4)

392 in_len += 4 - (in_len % 4);

393 result = dst = xmalloc(in_len / 4 * 3 + 1);

394 while (*src) {

395 int a = base64_digits[(unsigned char) base64dec_getc(&src)];

396 int b = base64_digits[(unsigned char) base64dec_getc(&src)];

397 int c = base64_digits[(unsigned char) base64dec_getc(&src)];

398 int d = base64_digits[(unsigned char) base64dec_getc(&src)];

399

400 /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */

401 if (a == -1 || b == -1)

402 break;

403

404 *dst++ = (a << 2) | ((b & 0x30) >> 4);

405 if (c == -1)

406 break;

407 *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2);

408 if (d == -1)

409 break;

410 *dst++ = ((c & 0x03) << 6) | d;

411 }

412 *dst = '\0';

413 return result;

414 }

415

416 void

417 selinit(void)

418 {

419 sel.mode = SEL_IDLE;

420 sel.snap = 0;

421 sel.ob.x = -1;

422 }

423

424 int

425 tlinelen(int y)

426 {

427 int i = term.col;

428

429 if (TLINE(y)[i - 1].mode & ATTR_WRAP)

430 return i;

431

432 while (i > 0 && TLINE(y)[i - 1].u == ' ')

433 --i;

434

435 return i;

436 }

437

438 void

439 selstart(int col, int row, int snap)

440 {

441 selclear();

442 sel.mode = SEL_EMPTY;

443 sel.type = SEL_REGULAR;

444 sel.alt = IS_SET(MODE_ALTSCREEN);

445 sel.snap = snap;

446 sel.oe.x = sel.ob.x = col;

447 sel.oe.y = sel.ob.y = row;

448 selnormalize();

449

450 if (sel.snap != 0)

451 sel.mode = SEL_READY;

452 tsetdirt(sel.nb.y, sel.ne.y);

453 }

454

455 void

456 selextend(int col, int row, int type, int done)

457 {

458 int oldey, oldex, oldsby, oldsey, oldtype;

459

460 if (sel.mode == SEL_IDLE)

461 return;

462 if (done && sel.mode == SEL_EMPTY) {

463 selclear();

464 return;

465 }

466

467 oldey = sel.oe.y;

468 oldex = sel.oe.x;

469 oldsby = sel.nb.y;

470 oldsey = sel.ne.y;

471 oldtype = sel.type;

472

473 sel.oe.x = col;

474 sel.oe.y = row;

475 selnormalize();

476 sel.type = type;

477

478 if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY)

479 tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));

480

481 sel.mode = done ? SEL_IDLE : SEL_READY;

482 }

483

484 void

485 selnormalize(void)

486 {

487 int i;

488

489 if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {

490 sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;

491 sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;

492 } else {

493 sel.nb.x = MIN(sel.ob.x, sel.oe.x);

494 sel.ne.x = MAX(sel.ob.x, sel.oe.x);

495 }

496 sel.nb.y = MIN(sel.ob.y, sel.oe.y);

497 sel.ne.y = MAX(sel.ob.y, sel.oe.y);

498

499 selsnap(&sel.nb.x, &sel.nb.y, -1);

500 selsnap(&sel.ne.x, &sel.ne.y, +1);

501

502 /* expand selection over line breaks */

503 if (sel.type == SEL_RECTANGULAR)

504 return;

505 i = tlinelen(sel.nb.y);

506 if (i < sel.nb.x)

507 sel.nb.x = i;

508 if (tlinelen(sel.ne.y) <= sel.ne.x)

509 sel.ne.x = term.col - 1;

510 }

511

512 int

513 selected(int x, int y)

514 {

515 if (sel.mode == SEL_EMPTY || sel.ob.x == -1 ||

516 sel.alt != IS_SET(MODE_ALTSCREEN))

517 return 0;

518

519 if (sel.type == SEL_RECTANGULAR)

520 return BETWEEN(y, sel.nb.y, sel.ne.y)

521 && BETWEEN(x, sel.nb.x, sel.ne.x);

522

523 return BETWEEN(y, sel.nb.y, sel.ne.y)

524 && (y != sel.nb.y || x >= sel.nb.x)

525 && (y != sel.ne.y || x <= sel.ne.x);

526 }

527

528 void

529 selsnap(int *x, int *y, int direction)

530 {

531 int newx, newy, xt, yt;

532 int delim, prevdelim;

533 const Glyph *gp, *prevgp;

534

535 switch (sel.snap) {

536 case SNAP_WORD:

537 /*

538 * Snap around if the word wraps around at the end or

539 * beginning of a line.

540 */

541 prevgp = &TLINE(*y)[*x];

542 prevdelim = ISDELIM(prevgp->u);

543 for (;;) {

544 newx = *x + direction;

545 newy = *y;

546 if (!BETWEEN(newx, 0, term.col - 1)) {

547 newy += direction;

548 newx = (newx + term.col) % term.col;

549 if (!BETWEEN(newy, 0, term.row - 1))

550 break;

551

552 if (direction > 0)

553 yt = *y, xt = *x;

554 else

555 yt = newy, xt = newx;

556 if (!(TLINE(yt)[xt].mode & ATTR_WRAP))

557 break;

558 }

559

560 if (newx >= tlinelen(newy))

561 break;

562

563 gp = &TLINE(newy)[newx];

564 delim = ISDELIM(gp->u);

565 if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim

566 || (delim && gp->u != prevgp->u)))

567 break;

568

569 *x = newx;

570 *y = newy;

571 prevgp = gp;

572 prevdelim = delim;

573 }

574 break;

575 case SNAP_LINE:

576 /*

577 * Snap around if the the previous line or the current one

578 * has set ATTR_WRAP at its end. Then the whole next or

579 * previous line will be selected.

580 */

581 *x = (direction < 0) ? 0 : term.col - 1;

582 if (direction < 0) {

583 for (; *y > 0; *y += direction) {

584 if (!(TLINE(*y-1)[term.col-1].mode

585 & ATTR_WRAP)) {

586 break;

587 }

588 }

589 } else if (direction > 0) {

590 for (; *y < term.row-1; *y += direction) {

591 if (!(TLINE(*y)[term.col-1].mode

592 & ATTR_WRAP)) {

593 break;

594 }

595 }

596 }

597 break;

598 }

599 }

600

601 char *

602 getsel(void)

603 {

604 char *str, *ptr;

605 int y, bufsize, lastx, linelen;

606 const Glyph *gp, *last;

607

608 if (sel.ob.x == -1)

609 return NULL;

610

611 bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ;

612 ptr = str = xmalloc(bufsize);

613

614 /* append every set & selected glyph to the selection */

615 for (y = sel.nb.y; y <= sel.ne.y; y++) {

616 if ((linelen = tlinelen(y)) == 0) {

617 *ptr++ = '\n';

618 continue;

619 }

620

621 if (sel.type == SEL_RECTANGULAR) {

622 gp = &TLINE(y)[sel.nb.x];

623 lastx = sel.ne.x;

624 } else {

625 gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];

626 lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;

627 }

628 last = &TLINE(y)[MIN(lastx, linelen-1)];

629 while (last >= gp && last->u == ' ')

630 --last;

631

632 for ( ; gp <= last; ++gp) {

633 if (gp->mode & ATTR_WDUMMY)

634 continue;

635

636 ptr += utf8encode(gp->u, ptr);

637 }

638

639 /*

640 * Copy and pasting of line endings is inconsistent

641 * in the inconsistent terminal and GUI world.

642 * The best solution seems like to produce '\n' when

643 * something is copied from st and convert '\n' to

644 * '\r', when something to be pasted is received by

645 * st.

646 * FIXME: Fix the computer world.

647 */

648 if ((y < sel.ne.y || lastx >= linelen) &&

649 (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))

650 *ptr++ = '\n';

651 }

652 *ptr = 0;

653 return str;

654 }

655

656 void

657 selclear(void)

658 {

659 if (sel.ob.x == -1)

660 return;

661 sel.mode = SEL_IDLE;

662 sel.ob.x = -1;

663 tsetdirt(sel.nb.y, sel.ne.y);

664 }

665

666 void

667 die(const char *errstr, ...)

668 {

669 va_list ap;

670

671 va_start(ap, errstr);

672 vfprintf(stderr, errstr, ap);

673 va_end(ap);

674 exit(1);

675 }

676

677 void

678 execsh(char *cmd, char **args)

679 {

680 char *sh, *prog, *arg;

681 const struct passwd *pw;

682

683 errno = 0;

684 if ((pw = getpwuid(getuid())) == NULL) {

685 if (errno)

686 die("getpwuid: %s\n", strerror(errno));

687 else

688 die("who are you?\n");

689 }

690

691 if ((sh = getenv("SHELL")) == NULL)

692 sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd;

693

694 if (args) {

695 prog = args[0];

696 arg = NULL;

697 } else if (scroll) {

698 prog = scroll;

699 arg = utmp ? utmp : sh;

700 } else if (utmp) {

701 prog = utmp;

702 arg = NULL;

703 } else {

704 prog = sh;

705 arg = NULL;

706 }

707 DEFAULT(args, ((char *[]) {prog, arg, NULL}));

708

709 unsetenv("COLUMNS");

710 unsetenv("LINES");

711 unsetenv("TERMCAP");

712 setenv("LOGNAME", pw->pw_name, 1);

713 setenv("USER", pw->pw_name, 1);

714 setenv("SHELL", sh, 1);

715 setenv("HOME", pw->pw_dir, 1);

716 setenv("TERM", termname, 1);

717

718 signal(SIGCHLD, SIG_DFL);

719 signal(SIGHUP, SIG_DFL);

720 signal(SIGINT, SIG_DFL);

721 signal(SIGQUIT, SIG_DFL);

722 signal(SIGTERM, SIG_DFL);

723 signal(SIGALRM, SIG_DFL);

724

725 execvp(prog, args);

726 _exit(1);

727 }

728

729 void

730 sigchld(int a)

731 {

732 int stat;

733 pid_t p;

734

735 if ((p = waitpid(pid, &stat, WNOHANG)) < 0)

736 die("waiting for pid %hd failed: %s\n", pid, strerror(errno));

737

738 if (pid != p)

739 return;

740

741 if (WIFEXITED(stat) && WEXITSTATUS(stat))

742 die("child exited with status %d\n", WEXITSTATUS(stat));

743 else if (WIFSIGNALED(stat))

744 die("child terminated due to signal %d\n", WTERMSIG(stat));

745 _exit(0);

746 }

747

748 void

749 stty(char **args)

750 {

751 char cmd[_POSIX_ARG_MAX], **p, *q, *s;

752 size_t n, siz;

753

754 if ((n = strlen(stty_args)) > sizeof(cmd)-1)

755 die("incorrect stty parameters\n");

756 memcpy(cmd, stty_args, n);

757 q = cmd + n;

758 siz = sizeof(cmd) - n;

759 for (p = args; p && (s = *p); ++p) {

760 if ((n = strlen(s)) > siz-1)

761 die("stty parameter length too long\n");

762 *q++ = ' ';

763 memcpy(q, s, n);

764 q += n;

765 siz -= n + 1;

766 }

767 *q = '\0';

768 if (system(cmd) != 0)

769 perror("Couldn't call stty");

770 }

771

772 int

773 ttynew(const char *line, char *cmd, const char *out, char **args)

774 {

775 int m, s;

776

777 if (out) {

778 term.mode |= MODE_PRINT;

779 iofd = (!strcmp(out, "-")) ?

780 1 : open(out, O_WRONLY | O_CREAT, 0666);

781 if (iofd < 0) {

782 #ifdef DEBUG

783 fprintf(stderr, "Error opening %s:%s\n",

784 out, strerror(errno));

785 #endif

786 }

787 }

788

789 if (line) {

790 if ((cmdfd = open(line, O_RDWR)) < 0)

791 die("open line '%s' failed: %s\n",

792 line, strerror(errno));

793 dup2(cmdfd, 0);

794 stty(args);

795 return cmdfd;

796 }

797

798 /* seems to work fine on linux, openbsd and freebsd */

799 if (openpty(&m, &s, NULL, NULL, NULL) < 0)

800 die("openpty failed: %s\n", strerror(errno));

801

802 switch (pid = fork()) {

803 case -1:

804 die("fork failed: %s\n", strerror(errno));

805 break;

806 case 0:

807 close(iofd);

808 close(m);

809 setsid(); /* create a new process group */

810 dup2(s, 0);

811 dup2(s, 1);

812 dup2(s, 2);

813 if (ioctl(s, TIOCSCTTY, NULL) < 0)

814 die("ioctl TIOCSCTTY failed: %s\n", strerror(errno));

815 if (s > 2)

816 close(s);

817 #ifdef __OpenBSD__

818 if (pledge("stdio getpw proc exec", NULL) == -1)

819 die("pledge\n");

820 #endif

821 execsh(cmd, args);

822 break;

823 default:

824 #ifdef __OpenBSD__

825 if (pledge("stdio rpath tty proc", NULL) == -1)

826 die("pledge\n");

827 #endif

828 close(s);

829 cmdfd = m;

830 signal(SIGCHLD, sigchld);

831 break;

832 }

833 return cmdfd;

834 }

835

836 size_t

837 ttyread(void)

838 {

839 static char buf[BUFSIZ];

840 static int buflen = 0;

841 int ret, written;

842

843 /* append read bytes to unprocessed bytes */

844 ret = read(cmdfd, buf+buflen, LEN(buf)-buflen);

845

846 switch (ret) {

847 case 0:

848 exit(0);

849 case -1:

850 die("couldn't read from shell: %s\n", strerror(errno));

851 default:

852 buflen += ret;

853 written = twrite(buf, buflen, 0);

854 buflen -= written;

855 /* keep any incomplete UTF-8 byte sequence for the next call */

856 if (buflen > 0)

857 memmove(buf, buf + written, buflen);

858 return ret;

859 }

860 }

861

862 void

863 ttywrite(const char *s, size_t n, int may_echo)

864 {

865 const char *next;

866 Arg arg = (Arg) { .i = term.scr };

867

868 kscrolldown(&arg);

869

870 if (may_echo && IS_SET(MODE_ECHO))

871 twrite(s, n, 1);

872

873 if (!IS_SET(MODE_CRLF)) {

874 ttywriteraw(s, n);

875 return;

876 }

877

878 /* This is similar to how the kernel handles ONLCR for ttys */

879 while (n > 0) {

880 if (*s == '\r') {

881 next = s + 1;

882 ttywriteraw("\r\n", 2);

883 } else {

884 next = memchr(s, '\r', n);

885 DEFAULT(next, s + n);

886 ttywriteraw(s, next - s);

887 }

888 n -= next - s;

889 s = next;

890 }

891 }

892

893 void

894 ttywriteraw(const char *s, size_t n)

895 {

896 fd_set wfd, rfd;

897 ssize_t r;

898 size_t lim = 256;

899

900 /*

901 * Remember that we are using a pty, which might be a modem line.

902 * Writing too much will clog the line. That's why we are doing this

903 * dance.

904 * FIXME: Migrate the world to Plan 9.

905 */

906 while (n > 0) {

907 FD_ZERO(&wfd);

908 FD_ZERO(&rfd);

909 FD_SET(cmdfd, &wfd);

910 FD_SET(cmdfd, &rfd);

911

912 /* Check if we can write. */

913 if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) {

914 if (errno == EINTR)

915 continue;

916 die("select failed: %s\n", strerror(errno));

917 }

918 if (FD_ISSET(cmdfd, &wfd)) {

919 /*

920 * Only write the bytes written by ttywrite() or the

921 * default of 256. This seems to be a reasonable value

922 * for a serial line. Bigger values might clog the I/O.

923 */

924 if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0)

925 goto write_error;

926 if (r < n) {

927 /*

928 * We weren't able to write out everything.

929 * This means the buffer is getting full

930 * again. Empty it.

931 */

932 if (n < lim)

933 lim = ttyread();

934 n -= r;

935 s += r;

936 } else {

937 /* All bytes have been written. */

938 break;

939 }

940 }

941 if (FD_ISSET(cmdfd, &rfd))

942 lim = ttyread();

943 }

944 return;

945

946 write_error:

947 die("write error on tty: %s\n", strerror(errno));

948 }

949

950 void

951 ttyresize(int tw, int th)

952 {

953 struct winsize w;

954

955 w.ws_row = term.row;

956 w.ws_col = term.col;

957 w.ws_xpixel = tw;

958 w.ws_ypixel = th;

959 if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) {

960 #ifdef DEBUG

961 fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno));

962 #endif

963 }

964 }

965

966 void

967 ttyhangup()

968 {

969 /* Send SIGHUP to shell */

970 kill(pid, SIGHUP);

971 }

972

973 int

974 tattrset(int attr)

975 {

976 int i, j;

977

978 for (i = 0; i < term.row-1; i++) {

979 for (j = 0; j < term.col-1; j++) {

980 if (term.line[i][j].mode & attr)

981 return 1;

982 }

983 }

984

985 return 0;

986 }

987

988 void

989 tsetdirt(int top, int bot)

990 {

991 int i;

992

993 LIMIT(top, 0, term.row-1);

994 LIMIT(bot, 0, term.row-1);

995

996 for (i = top; i <= bot; i++)

997 term.dirty[i] = 1;

998 }

999

1000 void

1001 tsetdirtattr(int attr)

1002 {

1003 int i, j;

1004

1005 for (i = 0; i < term.row-1; i++) {

1006 for (j = 0; j < term.col-1; j++) {

1007 if (term.line[i][j].mode & attr) {

1008 tsetdirt(i, i);

1009 break;

1010 }

1011 }

1012 }

1013 }

1014

1015 void

1016 tfulldirt(void)

1017 {

1018 tsetdirt(0, term.row-1);

1019 }

1020

1021 void

1022 tcursor(int mode)

1023 {

1024 static TCursor c[2];

1025 int alt = IS_SET(MODE_ALTSCREEN);

1026

1027 if (mode == CURSOR_SAVE) {

1028 c[alt] = term.c;

1029 } else if (mode == CURSOR_LOAD) {

1030 term.c = c[alt];

1031 tmoveto(c[alt].x, c[alt].y);

1032 }

1033 }

1034

1035 void

1036 treset(void)

1037 {

1038 uint i;

1039

1040 term.c = (TCursor){{

1041 .mode = ATTR_NULL,

1042 .fg = defaultfg,

1043 .bg = defaultbg

1044 }, .x = 0, .y = 0, .state = CURSOR_DEFAULT};

1045

1046 memset(term.tabs, 0, term.col * sizeof(*term.tabs));

1047 for (i = tabspaces; i < term.col; i += tabspaces)

1048 term.tabs[i] = 1;

1049 term.top = 0;

1050 term.bot = term.row - 1;

1051 term.mode = MODE_WRAP|MODE_UTF8;

1052 memset(term.trantbl, CS_USA, sizeof(term.trantbl));

1053 term.charset = 0;

1054

1055 for (i = 0; i < 2; i++) {

1056 tmoveto(0, 0);

1057 tcursor(CURSOR_SAVE);

1058 tclearregion(0, 0, term.col-1, term.row-1);

1059 tswapscreen();

1060 }

1061 }

1062

1063 void

1064 tnew(int col, int row)

1065 {

1066 term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };

1067 tresize(col, row);

1068 treset();

1069 }

1070

1071 int tisaltscr(void)

1072 {

1073 return IS_SET(MODE_ALTSCREEN);

1074 }

1075

1076 void

1077 tswapscreen(void)

1078 {

1079 Line *tmp = term.line;

1080

1081 term.line = term.alt;

1082 term.alt = tmp;

1083 term.mode ^= MODE_ALTSCREEN;

1084 tfulldirt();

1085 }

1086

1087 void

1088 kscrolldown(const Arg* a)

1089 {

1090 int n = a->i;

1091

1092 if (n < 0)

1093 n = term.row + n;

1094

1095 if (n > term.scr)

1096 n = term.scr;

1097

1098 if (term.scr > 0) {

1099 term.scr -= n;

1100 selscroll(0, -n);

1101 tfulldirt();

1102 }

1103 }

1104

1105 void

1106 kscrollup(const Arg* a)

1107 {

1108 if(term.histi-term.scr<=0) return;

1109 int n = a->i;

1110 //printf("%d %d %d\n",n,term.histi,term.scr);

1111 // term.histi/(term.histi-term.scr) -> % of the scroll

1112

1113 if (n < 0)

1114 n = term.row + n;

1115

1116 if(term.histi-term.scr-n<=0)

1117 n = term.histi-term.scr;

1118

1119 if (term.scr <= HISTSIZE-n) {

1120 term.scr += n;

1121 selscroll(0, n);

1122 tfulldirt();

1123 }

1124 }

1125

1126 void

1127 tscrolldown(int orig, int n, int copyhist)

1128 {

1129 int i;

1130 Line temp;

1131

1132 LIMIT(n, 0, term.bot-orig+1);

1133

1134 if (copyhist) {

1135 term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;

1136 temp = term.hist[term.histi];

1137 term.hist[term.histi] = term.line[term.bot];

1138 term.line[term.bot] = temp;

1139 }

1140

1141 tsetdirt(orig, term.bot-n);

1142 tclearregion(0, term.bot-n+1, term.col-1, term.bot);

1143

1144 for (i = term.bot; i >= orig+n; i--) {

1145 temp = term.line[i];

1146 term.line[i] = term.line[i-n];

1147 term.line[i-n] = temp;

1148 }

1149

1150 if (term.scr == 0)

1151 selscroll(orig, n);

1152 }

1153

1154 void

1155 tscrollup(int orig, int n, int copyhist)

1156 {

1157 int i;

1158 Line temp;

1159

1160 LIMIT(n, 0, term.bot-orig+1);

1161

1162 if (copyhist) {

1163 term.histi = (term.histi + 1) % HISTSIZE;

1164 temp = term.hist[term.histi];

1165 term.hist[term.histi] = term.line[orig];

1166 term.line[orig] = temp;

1167 }

1168

1169 if (term.scr > 0 && term.scr < HISTSIZE)

1170 term.scr = MIN(term.scr + n, HISTSIZE-1);

1171

1172 tclearregion(0, orig, term.col-1, orig+n-1);

1173 tsetdirt(orig+n, term.bot);

1174

1175 for (i = orig; i <= term.bot-n; i++) {

1176 temp = term.line[i];

1177 term.line[i] = term.line[i+n];

1178 term.line[i+n] = temp;

1179 }

1180

1181 if (term.scr == 0)

1182 selscroll(orig, -n);

1183 }

1184

1185 void

1186 selscroll(int orig, int n)

1187 {

1188 if (sel.ob.x == -1)

1189 return;

1190

1191 if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) {

1192 selclear();

1193 } else if (BETWEEN(sel.nb.y, orig, term.bot)) {

1194 sel.ob.y += n;

1195 sel.oe.y += n;

1196 if (sel.ob.y < term.top || sel.ob.y > term.bot ||

1197 sel.oe.y < term.top || sel.oe.y > term.bot) {

1198 selclear();

1199 } else {

1200 selnormalize();

1201 }

1202 }

1203 }

1204

1205 void

1206 tnewline(int first_col)

1207 {

1208 int y = term.c.y;

1209

1210 if (y == term.bot) {

1211 tscrollup(term.top, 1, 1);

1212 } else {

1213 y++;

1214 }

1215 tmoveto(first_col ? 0 : term.c.x, y);

1216 }

1217

1218 void

1219 csiparse(void)

1220 {

1221 char *p = csiescseq.buf, *np;

1222 long int v;

1223

1224 csiescseq.narg = 0;

1225 if (*p == '?') {

1226 csiescseq.priv = 1;

1227 p++;

1228 }

1229

1230 csiescseq.buf[csiescseq.len] = '\0';

1231 while (p < csiescseq.buf+csiescseq.len) {

1232 np = NULL;

1233 v = strtol(p, &np, 10);

1234 if (np == p)

1235 v = 0;

1236 if (v == LONG_MAX || v == LONG_MIN)

1237 v = -1;

1238 csiescseq.arg[csiescseq.narg++] = v;

1239 p = np;

1240 if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ)

1241 break;

1242 p++;

1243 }

1244 csiescseq.mode[0] = *p++;

1245 csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0';

1246 }

1247

1248 /* for absolute user moves, when decom is set */

1249 void

1250 tmoveato(int x, int y)

1251 {

1252 tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0));

1253 }

1254

1255 void

1256 tmoveto(int x, int y)

1257 {

1258 int miny, maxy;

1259

1260 if (term.c.state & CURSOR_ORIGIN) {

1261 miny = term.top;

1262 maxy = term.bot;

1263 } else {

1264 miny = 0;

1265 maxy = term.row - 1;

1266 }

1267 term.c.state &= ~CURSOR_WRAPNEXT;

1268 term.c.x = LIMIT(x, 0, term.col-1);

1269 term.c.y = LIMIT(y, miny, maxy);

1270 }

1271

1272 void

1273 tsetchar(Rune u, const Glyph *attr, int x, int y)

1274 {

1275 static const char *vt100_0[62] = { /* 0x41 - 0x7e */

1276 "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */

1277 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */

1278 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */

1279 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */

1280 "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */

1281 "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */

1282 "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */

1283 "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */

1284 };

1285

1286 /*

1287 * The table is proudly stolen from rxvt.

1288 */

1289 if (term.trantbl[term.charset] == CS_GRAPHIC0 &&

1290 BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])

1291 utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);

1292

1293 if (term.line[y][x].mode & ATTR_WIDE) {

1294 if (x+1 < term.col) {

1295 term.line[y][x+1].u = ' ';

1296 term.line[y][x+1].mode &= ~ATTR_WDUMMY;

1297 }

1298 } else if (term.line[y][x].mode & ATTR_WDUMMY) {

1299 term.line[y][x-1].u = ' ';

1300 term.line[y][x-1].mode &= ~ATTR_WIDE;

1301 }

1302

1303 term.dirty[y] = 1;

1304 term.line[y][x] = *attr;

1305 term.line[y][x].u = u;

1306 }

1307

1308 void

1309 tclearregion(int x1, int y1, int x2, int y2)

1310 {

1311 int x, y, temp;

1312 Glyph *gp;

1313

1314 if (x1 > x2)

1315 temp = x1, x1 = x2, x2 = temp;

1316 if (y1 > y2)

1317 temp = y1, y1 = y2, y2 = temp;

1318

1319 LIMIT(x1, 0, term.col-1);

1320 LIMIT(x2, 0, term.col-1);

1321 LIMIT(y1, 0, term.row-1);

1322 LIMIT(y2, 0, term.row-1);

1323

1324 for (y = y1; y <= y2; y++) {

1325 term.dirty[y] = 1;

1326 for (x = x1; x <= x2; x++) {

1327 gp = &term.line[y][x];

1328 if (selected(x, y))

1329 selclear();

1330 gp->fg = term.c.attr.fg;

1331 gp->bg = term.c.attr.bg;

1332 gp->mode = 0;

1333 gp->u = ' ';

1334 }

1335 }

1336 }

1337

1338 void

1339 tdeletechar(int n)

1340 {

1341 int dst, src, size;

1342 Glyph *line;

1343

1344 LIMIT(n, 0, term.col - term.c.x);

1345

1346 dst = term.c.x;

1347 src = term.c.x + n;

1348 size = term.col - src;

1349 line = term.line[term.c.y];

1350

1351 memmove(&line[dst], &line[src], size * sizeof(Glyph));

1352 tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);

1353 }

1354

1355 void

1356 tinsertblank(int n)

1357 {

1358 int dst, src, size;

1359 Glyph *line;

1360

1361 LIMIT(n, 0, term.col - term.c.x);

1362

1363 dst = term.c.x + n;

1364 src = term.c.x;

1365 size = term.col - dst;

1366 line = term.line[term.c.y];

1367

1368 memmove(&line[dst], &line[src], size * sizeof(Glyph));

1369 tclearregion(src, term.c.y, dst - 1, term.c.y);

1370 }

1371

1372 void

1373 tinsertblankline(int n)

1374 {

1375 if (BETWEEN(term.c.y, term.top, term.bot))

1376 tscrolldown(term.c.y, n, 0);

1377 }

1378

1379 void

1380 tdeleteline(int n)

1381 {

1382 if (BETWEEN(term.c.y, term.top, term.bot))

1383 tscrollup(term.c.y, n, 0);

1384 }

1385

1386 int32_t

1387 tdefcolor(const int *attr, int *npar, int l)

1388 {

1389 int32_t idx = -1;

1390 uint r, g, b;

1391

1392 switch (attr[*npar + 1]) {

1393 case 2: /* direct color in RGB space */

1394 if (*npar + 4 >= l) {

1395 #ifdef DEBUG

1396 fprintf(stderr,

1397 "erresc(38): Incorrect number of parameters (%d)\n",

1398 *npar);

1399 #endif

1400 break;

1401 }

1402 r = attr[*npar + 2];

1403 g = attr[*npar + 3];

1404 b = attr[*npar + 4];

1405 *npar += 4;

1406 if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255)) {

1407 #ifdef DEBUG

1408 fprintf(stderr, "erresc: bad rgb color (%u,%u,%u)\n",

1409 r, g, b);

1410 #endif

1411 }

1412 else

1413 idx = TRUECOLOR(r, g, b);

1414 break;

1415 case 5: /* indexed color */

1416 if (*npar + 2 >= l) {

1417 #ifdef DEBUG

1418 fprintf(stderr,

1419 "erresc(38): Incorrect number of parameters (%d)\n",

1420 *npar);

1421 #endif

1422 break;

1423 }

1424 *npar += 2;

1425 if (!BETWEEN(attr[*npar], 0, 255)) {

1426 #ifdef DEBUG

1427 fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]);

1428 #endif

1429 }

1430 else

1431 idx = attr[*npar];

1432 break;

1433 case 0: /* implemented defined (only foreground) */

1434 case 1: /* transparent */

1435 case 3: /* direct color in CMY space */

1436 case 4: /* direct color in CMYK space */

1437 default:

1438 #ifdef DEBUG

1439 fprintf(stderr,

1440 "erresc(38): gfx attr %d unknown\n", attr[*npar]);

1441 #endif

1442 break;

1443 }

1444

1445 return idx;

1446 }

1447

1448 void

1449 tsetattr(const int *attr, int l)

1450 {

1451 int i;

1452 int32_t idx;

1453

1454 for (i = 0; i < l; i++) {

1455 switch (attr[i]) {

1456 case 0:

1457 term.c.attr.mode &= ~(

1458 ATTR_BOLD |

1459 ATTR_FAINT |

1460 ATTR_ITALIC |

1461 ATTR_UNDERLINE |

1462 ATTR_BLINK |

1463 ATTR_REVERSE |

1464 ATTR_INVISIBLE |

1465 ATTR_STRUCK );

1466 term.c.attr.fg = defaultfg;

1467 term.c.attr.bg = defaultbg;

1468 break;

1469 case 1:

1470 term.c.attr.mode |= ATTR_BOLD;

1471 break;

1472 case 2:

1473 term.c.attr.mode |= ATTR_FAINT;

1474 break;

1475 case 3:

1476 term.c.attr.mode |= ATTR_ITALIC;

1477 break;

1478 case 4:

1479 term.c.attr.mode |= ATTR_UNDERLINE;

1480 break;

1481 case 5: /* slow blink */

1482 /* FALLTHROUGH */

1483 case 6: /* rapid blink */

1484 term.c.attr.mode |= ATTR_BLINK;

1485 break;

1486 case 7:

1487 term.c.attr.mode |= ATTR_REVERSE;

1488 break;

1489 case 8:

1490 term.c.attr.mode |= ATTR_INVISIBLE;

1491 break;

1492 case 9:

1493 term.c.attr.mode |= ATTR_STRUCK;

1494 break;

1495 case 22:

1496 term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT);

1497 break;

1498 case 23:

1499 term.c.attr.mode &= ~ATTR_ITALIC;

1500 break;

1501 case 24:

1502 term.c.attr.mode &= ~ATTR_UNDERLINE;

1503 break;

1504 case 25:

1505 term.c.attr.mode &= ~ATTR_BLINK;

1506 break;

1507 case 27:

1508 term.c.attr.mode &= ~ATTR_REVERSE;

1509 break;

1510 case 28:

1511 term.c.attr.mode &= ~ATTR_INVISIBLE;

1512 break;

1513 case 29:

1514 term.c.attr.mode &= ~ATTR_STRUCK;

1515 break;

1516 case 38:

1517 if ((idx = tdefcolor(attr, &i, l)) >= 0)

1518 term.c.attr.fg = idx;

1519 break;

1520 case 39:

1521 term.c.attr.fg = defaultfg;

1522 break;

1523 case 48:

1524 if ((idx = tdefcolor(attr, &i, l)) >= 0)

1525 term.c.attr.bg = idx;

1526 break;

1527 case 49:

1528 term.c.attr.bg = defaultbg;

1529 break;

1530 default:

1531 if (BETWEEN(attr[i], 30, 37)) {

1532 term.c.attr.fg = attr[i] - 30;

1533 } else if (BETWEEN(attr[i], 40, 47)) {

1534 term.c.attr.bg = attr[i] - 40;

1535 } else if (BETWEEN(attr[i], 90, 97)) {

1536 term.c.attr.fg = attr[i] - 90 + 8;

1537 } else if (BETWEEN(attr[i], 100, 107)) {

1538 term.c.attr.bg = attr[i] - 100 + 8;

1539 } else {

1540 #ifdef DEBUG

1541 fprintf(stderr,

1542 "erresc(default): gfx attr %d unknown\n",

1543 attr[i]);

1544 csidump();

1545 #endif

1546 }

1547 break;

1548 }

1549 }

1550 }

1551

1552 void

1553 tsetscroll(int t, int b)

1554 {

1555 int temp;

1556

1557 LIMIT(t, 0, term.row-1);

1558 LIMIT(b, 0, term.row-1);

1559 if (t > b) {

1560 temp = t;

1561 t = b;

1562 b = temp;

1563 }

1564 term.top = t;

1565 term.bot = b;

1566 }

1567

1568 void

1569 tsetmode(int priv, int set, const int *args, int narg)

1570 {

1571 int alt; const int *lim;

1572

1573 for (lim = args + narg; args < lim; ++args) {

1574 if (priv) {

1575 switch (*args) {

1576 case 1: /* DECCKM -- Cursor key */

1577 xsetmode(set, MODE_APPCURSOR);

1578 break;

1579 case 5: /* DECSCNM -- Reverse video */

1580 xsetmode(set, MODE_REVERSE);

1581 break;

1582 case 6: /* DECOM -- Origin */

1583 MODBIT(term.c.state, set, CURSOR_ORIGIN);

1584 tmoveato(0, 0);

1585 break;

1586 case 7: /* DECAWM -- Auto wrap */

1587 MODBIT(term.mode, set, MODE_WRAP);

1588 break;

1589 case 0: /* Error (IGNORED) */

1590 case 2: /* DECANM -- ANSI/VT52 (IGNORED) */

1591 case 3: /* DECCOLM -- Column (IGNORED) */

1592 case 4: /* DECSCLM -- Scroll (IGNORED) */

1593 case 8: /* DECARM -- Auto repeat (IGNORED) */

1594 case 18: /* DECPFF -- Printer feed (IGNORED) */

1595 case 19: /* DECPEX -- Printer extent (IGNORED) */

1596 case 42: /* DECNRCM -- National characters (IGNORED) */

1597 case 12: /* att610 -- Start blinking cursor (IGNORED) */

1598 break;

1599 case 25: /* DECTCEM -- Text Cursor Enable Mode */

1600 xsetmode(!set, MODE_HIDE);

1601 break;

1602 case 9: /* X10 mouse compatibility mode */

1603 xsetpointermotion(0);

1604 xsetmode(0, MODE_MOUSE);

1605 xsetmode(set, MODE_MOUSEX10);

1606 break;

1607 case 1000: /* 1000: report button press */

1608 xsetpointermotion(0);

1609 xsetmode(0, MODE_MOUSE);

1610 xsetmode(set, MODE_MOUSEBTN);

1611 break;

1612 case 1002: /* 1002: report motion on button press */

1613 xsetpointermotion(0);

1614 xsetmode(0, MODE_MOUSE);

1615 xsetmode(set, MODE_MOUSEMOTION);

1616 break;

1617 case 1003: /* 1003: enable all mouse motions */

1618 xsetpointermotion(set);

1619 xsetmode(0, MODE_MOUSE);

1620 xsetmode(set, MODE_MOUSEMANY);

1621 break;

1622 case 1004: /* 1004: send focus events to tty */

1623 xsetmode(set, MODE_FOCUS);

1624 break;

1625 case 1006: /* 1006: extended reporting mode */

1626 xsetmode(set, MODE_MOUSESGR);

1627 break;

1628 case 1034:

1629 xsetmode(set, MODE_8BIT);

1630 break;

1631 case 1049: /* swap screen & set/restore cursor as xterm */

1632 if (!allowaltscreen)

1633 break;

1634 tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);

1635 /* FALLTHROUGH */

1636 case 47: /* swap screen */

1637 case 1047:

1638 if (!allowaltscreen)

1639 break;

1640 alt = IS_SET(MODE_ALTSCREEN);

1641 if (alt) {

1642 tclearregion(0, 0, term.col-1,

1643 term.row-1);

1644 }

1645 if (set ^ alt) /* set is always 1 or 0 */

1646 tswapscreen();

1647 if (*args != 1049)

1648 break;

1649 /* FALLTHROUGH */

1650 case 1048:

1651 tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);

1652 break;

1653 case 2004: /* 2004: bracketed paste mode */

1654 xsetmode(set, MODE_BRCKTPASTE);

1655 break;

1656 /* Not implemented mouse modes. See comments there. */

1657 case 1001: /* mouse highlight mode; can hang the

1658 terminal by design when implemented. */

1659 case 1005: /* UTF-8 mouse mode; will confuse

1660 applications not supporting UTF-8

1661 and luit. */

1662 case 1015: /* urxvt mangled mouse mode; incompatible

1663 and can be mistaken for other control

1664 codes. */

1665 break;

1666 default:

1667 #ifdef DEBUG

1668 fprintf(stderr,

1669 "erresc: unknown private set/reset mode %d\n",

1670 *args);

1671 #endif

1672 break;

1673 }

1674 } else {

1675 switch (*args) {

1676 case 0: /* Error (IGNORED) */

1677 break;

1678 case 2:

1679 xsetmode(set, MODE_KBDLOCK);

1680 break;

1681 case 4: /* IRM -- Insertion-replacement */

1682 MODBIT(term.mode, set, MODE_INSERT);

1683 break;

1684 case 12: /* SRM -- Send/Receive */

1685 MODBIT(term.mode, !set, MODE_ECHO);

1686 break;

1687 case 20: /* LNM -- Linefeed/new line */

1688 MODBIT(term.mode, set, MODE_CRLF);

1689 break;

1690 default:

1691 #ifdef DEBUG

1692 fprintf(stderr,

1693 "erresc: unknown set/reset mode %d\n",

1694 *args);

1695 #endif

1696 break;

1697 }

1698 }

1699 }

1700 }

1701

1702 void

1703 csihandle(void)

1704 {

1705 char buf[40];

1706 int len;

1707

1708 switch (csiescseq.mode[0]) {

1709 default:

1710 unknown:

1711 #ifdef DEBUG

1712 fprintf(stderr, "erresc: unknown csi ");

1713 csidump();

1714 #endif

1715 /* die(""); */

1716 break;

1717 case '@': /* ICH -- Insert <n> blank char */

1718 DEFAULT(csiescseq.arg[0], 1);

1719 tinsertblank(csiescseq.arg[0]);

1720 break;

1721 case 'A': /* CUU -- Cursor <n> Up */

1722 DEFAULT(csiescseq.arg[0], 1);

1723 tmoveto(term.c.x, term.c.y-csiescseq.arg[0]);

1724 break;

1725 case 'B': /* CUD -- Cursor <n> Down */

1726 case 'e': /* VPR --Cursor <n> Down */

1727 DEFAULT(csiescseq.arg[0], 1);

1728 tmoveto(term.c.x, term.c.y+csiescseq.arg[0]);

1729 break;

1730 case 'i': /* MC -- Media Copy */

1731 switch (csiescseq.arg[0]) {

1732 case 0:

1733 tdump();

1734 break;

1735 case 1:

1736 tdumpline(term.c.y);

1737 break;

1738 case 2:

1739 tdumpsel();

1740 break;

1741 case 4:

1742 term.mode &= ~MODE_PRINT;

1743 break;

1744 case 5:

1745 term.mode |= MODE_PRINT;

1746 break;

1747 }

1748 break;

1749 case 'c': /* DA -- Device Attributes */

1750 if (csiescseq.arg[0] == 0)

1751 ttywrite(vtiden, strlen(vtiden), 0);

1752 break;

1753 case 'b': /* REP -- if last char is printable print it <n> more times */

1754 DEFAULT(csiescseq.arg[0], 1);

1755 if (term.lastc)

1756 while (csiescseq.arg[0]-- > 0)

1757 tputc(term.lastc);

1758 break;

1759 case 'C': /* CUF -- Cursor <n> Forward */

1760 case 'a': /* HPR -- Cursor <n> Forward */

1761 DEFAULT(csiescseq.arg[0], 1);

1762 tmoveto(term.c.x+csiescseq.arg[0], term.c.y);

1763 break;

1764 case 'D': /* CUB -- Cursor <n> Backward */

1765 DEFAULT(csiescseq.arg[0], 1);

1766 tmoveto(term.c.x-csiescseq.arg[0], term.c.y);

1767 break;

1768 case 'E': /* CNL -- Cursor <n> Down and first col */

1769 DEFAULT(csiescseq.arg[0], 1);

1770 tmoveto(0, term.c.y+csiescseq.arg[0]);

1771 break;

1772 case 'F': /* CPL -- Cursor <n> Up and first col */

1773 DEFAULT(csiescseq.arg[0], 1);

1774 tmoveto(0, term.c.y-csiescseq.arg[0]);

1775 break;

1776 case 'g': /* TBC -- Tabulation clear */

1777 switch (csiescseq.arg[0]) {

1778 case 0: /* clear current tab stop */

1779 term.tabs[term.c.x] = 0;

1780 break;

1781 case 3: /* clear all the tabs */

1782 memset(term.tabs, 0, term.col * sizeof(*term.tabs));

1783 break;

1784 default:

1785 goto unknown;

1786 }

1787 break;

1788 case 'G': /* CHA -- Move to <col> */

1789 case '`': /* HPA */

1790 DEFAULT(csiescseq.arg[0], 1);

1791 tmoveto(csiescseq.arg[0]-1, term.c.y);

1792 break;

1793 case 'H': /* CUP -- Move to <row> <col> */

1794 case 'f': /* HVP */

1795 DEFAULT(csiescseq.arg[0], 1);

1796 DEFAULT(csiescseq.arg[1], 1);

1797 tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1);

1798 break;

1799 case 'I': /* CHT -- Cursor Forward Tabulation <n> tab stops */

1800 DEFAULT(csiescseq.arg[0], 1);

1801 tputtab(csiescseq.arg[0]);

1802 break;

1803 case 'J': /* ED -- Clear screen */

1804 switch (csiescseq.arg[0]) {

1805 case 0: /* below */

1806 tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);

1807 if (term.c.y < term.row-1) {

1808 tclearregion(0, term.c.y+1, term.col-1,

1809 term.row-1);

1810 }

1811 break;

1812 case 1: /* above */

1813 if (term.c.y > 1)

1814 tclearregion(0, 0, term.col-1, term.c.y-1);

1815 tclearregion(0, term.c.y, term.c.x, term.c.y);

1816 break;

1817 case 2: /* all */

1818 tclearregion(0, 0, term.col-1, term.row-1);

1819 break;

1820 default:

1821 goto unknown;

1822 }

1823 break;

1824 case 'K': /* EL -- Clear line */

1825 switch (csiescseq.arg[0]) {

1826 case 0: /* right */

1827 tclearregion(term.c.x, term.c.y, term.col-1,

1828 term.c.y);

1829 break;

1830 case 1: /* left */

1831 tclearregion(0, term.c.y, term.c.x, term.c.y);

1832 break;

1833 case 2: /* all */

1834 tclearregion(0, term.c.y, term.col-1, term.c.y);

1835 break;

1836 }

1837 break;

1838 case 'S': /* SU -- Scroll <n> line up */

1839 DEFAULT(csiescseq.arg[0], 1);

1840 tscrollup(term.top, csiescseq.arg[0], 0);

1841 break;

1842 case 'T': /* SD -- Scroll <n> line down */

1843 DEFAULT(csiescseq.arg[0], 1);

1844 tscrolldown(term.top, csiescseq.arg[0], 0);

1845 break;

1846 case 'L': /* IL -- Insert <n> blank lines */

1847 DEFAULT(csiescseq.arg[0], 1);

1848 tinsertblankline(csiescseq.arg[0]);

1849 break;

1850 case 'l': /* RM -- Reset Mode */

1851 tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg);

1852 break;

1853 case 'M': /* DL -- Delete <n> lines */

1854 DEFAULT(csiescseq.arg[0], 1);

1855 tdeleteline(csiescseq.arg[0]);

1856 break;

1857 case 'X': /* ECH -- Erase <n> char */

1858 DEFAULT(csiescseq.arg[0], 1);

1859 tclearregion(term.c.x, term.c.y,

1860 term.c.x + csiescseq.arg[0] - 1, term.c.y);

1861 break;

1862 case 'P': /* DCH -- Delete <n> char */

1863 DEFAULT(csiescseq.arg[0], 1);

1864 tdeletechar(csiescseq.arg[0]);

1865 break;

1866 case 'Z': /* CBT -- Cursor Backward Tabulation <n> tab stops */

1867 DEFAULT(csiescseq.arg[0], 1);

1868 tputtab(-csiescseq.arg[0]);

1869 break;

1870 case 'd': /* VPA -- Move to <row> */

1871 DEFAULT(csiescseq.arg[0], 1);

1872 tmoveato(term.c.x, csiescseq.arg[0]-1);

1873 break;

1874 case 'h': /* SM -- Set terminal mode */

1875 tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg);

1876 break;

1877 case 'm': /* SGR -- Terminal attribute (color) */

1878 tsetattr(csiescseq.arg, csiescseq.narg);

1879 break;

1880 case 'n': /* DSR – Device Status Report (cursor position) */

1881 if (csiescseq.arg[0] == 6) {

1882 len = snprintf(buf, sizeof(buf), "\033[%i;%iR",

1883 term.c.y+1, term.c.x+1);

1884 ttywrite(buf, len, 0);

1885 }

1886 break;

1887 case 'r': /* DECSTBM -- Set Scrolling Region */

1888 if (csiescseq.priv) {

1889 goto unknown;

1890 } else {

1891 DEFAULT(csiescseq.arg[0], 1);

1892 DEFAULT(csiescseq.arg[1], term.row);

1893 tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1);

1894 tmoveato(0, 0);

1895 }

1896 break;

1897 case 's': /* DECSC -- Save cursor position (ANSI.SYS) */

1898 tcursor(CURSOR_SAVE);

1899 break;

1900 case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */

1901 tcursor(CURSOR_LOAD);

1902 break;

1903 case ' ':

1904 switch (csiescseq.mode[1]) {

1905 case 'q': /* DECSCUSR -- Set Cursor Style */

1906 if (xsetcursor(csiescseq.arg[0]))

1907 goto unknown;

1908 break;

1909 default:

1910 goto unknown;

1911 }

1912 break;

1913 }

1914 }

1915

1916 void

1917 csidump(void)

1918 {

1919 size_t i;

1920 uint c;

1921

1922 fprintf(stderr, "ESC[");

1923 for (i = 0; i < csiescseq.len; i++) {

1924 c = csiescseq.buf[i] & 0xff;

1925 if (isprint(c)) {

1926 putc(c, stderr);

1927 } else if (c == '\n') {

1928 fprintf(stderr, "(\\n)");

1929 } else if (c == '\r') {

1930 fprintf(stderr, "(\\r)");

1931 } else if (c == 0x1b) {

1932 fprintf(stderr, "(\\e)");

1933 } else {

1934 fprintf(stderr, "(%02x)", c);

1935 }

1936 }

1937 putc('\n', stderr);

1938 }

1939

1940 void

1941 csireset(void)

1942 {

1943 memset(&csiescseq, 0, sizeof(csiescseq));

1944 }

1945

1946 void

1947 strhandle(void)

1948 {

1949 char *p = NULL, *dec;

1950 int j, narg, par;

1951

1952 term.esc &= ~(ESC_STR_END|ESC_STR);

1953 strparse();

1954 par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0;

1955

1956 switch (strescseq.type) {

1957 case ']': /* OSC -- Operating System Command */

1958 switch (par) {

1959 case 0:

1960 if (narg > 1) {

1961 xsettitle(strescseq.args[1]);

1962 xseticontitle(strescseq.args[1]);

1963 }

1964 return;

1965 case 1:

1966 if (narg > 1)

1967 xseticontitle(strescseq.args[1]);

1968 return;

1969 case 2:

1970 if (narg > 1)

1971 xsettitle(strescseq.args[1]);

1972 return;

1973 case 52:

1974 if (narg > 2 && allowwindowops) {

1975 dec = base64dec(strescseq.args[2]);

1976 if (dec) {

1977 xsetsel(dec);

1978 xclipcopy();

1979 } else {

1980 #ifdef DEBUG

1981 fprintf(stderr, "erresc: invalid base64\n");

1982 #endif

1983 }

1984 }

1985 return;

1986 case 4: /* color set */

1987 if (narg < 3)

1988 break;

1989 p = strescseq.args[2];

1990 /* FALLTHROUGH */

1991 case 104: /* color reset, here p = NULL */

1992 j = (narg > 1) ? atoi(strescseq.args[1]) : -1;

1993 if (xsetcolorname(j, p)) {

1994 if (par == 104 && narg <= 1)

1995 return; /* color reset without parameter */

1996 fprintf(stderr, "erresc: invalid color j=%d, p=%s\n",

1997 j, p ? p : "(null)");

1998 } else {

1999 /*

2000 * TODO if defaultbg color is changed, borders

2001 * are dirty

2002 */

2003 redraw();

2004 }

2005 return;

2006 }

2007 break;

2008 case 'k': /* old title set compatibility */

2009 xsettitle(strescseq.args[0]);

2010 return;

2011 case 'P': /* DCS -- Device Control String */

2012 case '_': /* APC -- Application Program Command */

2013 case '^': /* PM -- Privacy Message */

2014 return;

2015 }

2016

2017 #ifdef DEBUG

2018 fprintf(stderr, "erresc: unknown str ");

2019 strdump();

2020 #endif

2021 }

2022

2023 void

2024 strparse(void)

2025 {

2026 int c;

2027 char *p = strescseq.buf;

2028

2029 strescseq.narg = 0;

2030 strescseq.buf[strescseq.len] = '\0';

2031

2032 if (*p == '\0')

2033 return;

2034

2035 while (strescseq.narg < STR_ARG_SIZ) {

2036 strescseq.args[strescseq.narg++] = p;

2037 while ((c = *p) != ';' && c != '\0')

2038 ++p;

2039 if (c == '\0')

2040 return;

2041 *p++ = '\0';

2042 }

2043 }

2044

2045 void

2046 strdump(void)

2047 {

2048 size_t i;

2049 uint c;

2050

2051 fprintf(stderr, "ESC%c", strescseq.type);

2052 for (i = 0; i < strescseq.len; i++) {

2053 c = strescseq.buf[i] & 0xff;

2054 if (c == '\0') {

2055 putc('\n', stderr);

2056 return;

2057 } else if (isprint(c)) {

2058 putc(c, stderr);

2059 } else if (c == '\n') {

2060 fprintf(stderr, "(\\n)");

2061 } else if (c == '\r') {

2062 fprintf(stderr, "(\\r)");

2063 } else if (c == 0x1b) {

2064 fprintf(stderr, "(\\e)");

2065 } else {

2066 fprintf(stderr, "(%02x)", c);

2067 }

2068 }

2069 fprintf(stderr, "ESC\\\n");

2070 }

2071

2072 void

2073 strreset(void)

2074 {

2075 strescseq = (STREscape){

2076 .buf = xrealloc(strescseq.buf, STR_BUF_SIZ),

2077 .siz = STR_BUF_SIZ,

2078 };

2079 }

2080

2081 void

2082 sendbreak(const Arg *arg)

2083 {

2084 if (tcsendbreak(cmdfd, 0))

2085 perror("Error sending break");

2086 }

2087

2088 void

2089 tprinter(char *s, size_t len)

2090 {

2091 if (iofd != -1 && xwrite(iofd, s, len) < 0) {

2092 perror("Error writing to output file");

2093 close(iofd);

2094 iofd = -1;

2095 }

2096 }

2097

2098 void

2099 toggleprinter(const Arg *arg)

2100 {

2101 term.mode ^= MODE_PRINT;

2102 }

2103

2104 void

2105 printscreen(const Arg *arg)

2106 {

2107 tdump();

2108 }

2109

2110 void

2111 printsel(const Arg *arg)

2112 {

2113 tdumpsel();

2114 }

2115

2116 void

2117 tdumpsel(void)

2118 {

2119 char *ptr;

2120

2121 if ((ptr = getsel())) {

2122 tprinter(ptr, strlen(ptr));

2123 free(ptr);

2124 }

2125 }

2126

2127 void

2128 tdumpline(int n)

2129 {

2130 char buf[UTF_SIZ];

2131 const Glyph *bp, *end;

2132

2133 bp = &term.line[n][0];

2134 end = &bp[MIN(tlinelen(n), term.col) - 1];

2135 if (bp != end || bp->u != ' ') {

2136 for ( ; bp <= end; ++bp)

2137 tprinter(buf, utf8encode(bp->u, buf));

2138 }

2139 tprinter("\n", 1);

2140 }

2141

2142 void

2143 tdump(void)

2144 {

2145 int i;

2146

2147 for (i = 0; i < term.row; ++i)

2148 tdumpline(i);

2149 }

2150

2151 void

2152 tputtab(int n)

2153 {

2154 uint x = term.c.x;

2155

2156 if (n > 0) {

2157 while (x < term.col && n--)

2158 for (++x; x < term.col && !term.tabs[x]; ++x)

2159 /* nothing */ ;

2160 } else if (n < 0) {

2161 while (x > 0 && n++)

2162 for (--x; x > 0 && !term.tabs[x]; --x)

2163 /* nothing */ ;

2164 }

2165 term.c.x = LIMIT(x, 0, term.col-1);

2166 }

2167

2168 void

2169 tdefutf8(char ascii)

2170 {

2171 if (ascii == 'G')

2172 term.mode |= MODE_UTF8;

2173 else if (ascii == '@')

2174 term.mode &= ~MODE_UTF8;

2175 }

2176

2177 void

2178 tdeftran(char ascii)

2179 {

2180 static char cs[] = "0B";

2181 static int vcs[] = {CS_GRAPHIC0, CS_USA};

2182 char *p;

2183

2184 if ((p = strchr(cs, ascii)) == NULL) {

2185 #ifdef DEBUG

2186 fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii);

2187 #endif

2188 } else {

2189 term.trantbl[term.icharset] = vcs[p - cs];

2190 }

2191 }

2192

2193 void

2194 tdectest(char c)

2195 {

2196 int x, y;

2197

2198 if (c == '8') { /* DEC screen alignment test. */

2199 for (x = 0; x < term.col; ++x) {

2200 for (y = 0; y < term.row; ++y)

2201 tsetchar('E', &term.c.attr, x, y);

2202 }

2203 }

2204 }

2205

2206 void

2207 tstrsequence(uchar c)

2208 {

2209 switch (c) {

2210 case 0x90: /* DCS -- Device Control String */

2211 c = 'P';

2212 break;

2213 case 0x9f: /* APC -- Application Program Command */

2214 c = '_';

2215 break;

2216 case 0x9e: /* PM -- Privacy Message */

2217 c = '^';

2218 break;

2219 case 0x9d: /* OSC -- Operating System Command */

2220 c = ']';

2221 break;

2222 }

2223 strreset();

2224 strescseq.type = c;

2225 term.esc |= ESC_STR;

2226 }

2227

2228 void

2229 tcontrolcode(uchar ascii)

2230 {

2231 switch (ascii) {

2232 case '\t': /* HT */

2233 tputtab(1);

2234 return;

2235 case '\b': /* BS */

2236 tmoveto(term.c.x-1, term.c.y);

2237 return;

2238 case '\r': /* CR */

2239 tmoveto(0, term.c.y);

2240 return;

2241 case '\f': /* LF */

2242 case '\v': /* VT */

2243 case '\n': /* LF */

2244 /* go to first col if the mode is set */

2245 tnewline(IS_SET(MODE_CRLF));

2246 return;

2247 case '\a': /* BEL */

2248 if (term.esc & ESC_STR_END) {

2249 /* backwards compatibility to xterm */

2250 strhandle();

2251 } else {

2252 xbell();

2253 }

2254 break;

2255 case '\033': /* ESC */

2256 csireset();

2257 term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST);

2258 term.esc |= ESC_START;

2259 return;

2260 case '\016': /* SO (LS1 -- Locking shift 1) */

2261 case '\017': /* SI (LS0 -- Locking shift 0) */

2262 term.charset = 1 - (ascii - '\016');

2263 return;

2264 case '\032': /* SUB */

2265 tsetchar('?', &term.c.attr, term.c.x, term.c.y);

2266 /* FALLTHROUGH */

2267 case '\030': /* CAN */

2268 csireset();

2269 break;

2270 case '\005': /* ENQ (IGNORED) */

2271 case '\000': /* NUL (IGNORED) */

2272 case '\021': /* XON (IGNORED) */

2273 case '\023': /* XOFF (IGNORED) */

2274 case 0177: /* DEL (IGNORED) */

2275 return;

2276 case 0x80: /* TODO: PAD */

2277 case 0x81: /* TODO: HOP */

2278 case 0x82: /* TODO: BPH */

2279 case 0x83: /* TODO: NBH */

2280 case 0x84: /* TODO: IND */

2281 break;

2282 case 0x85: /* NEL -- Next line */

2283 tnewline(1); /* always go to first col */

2284 break;

2285 case 0x86: /* TODO: SSA */

2286 case 0x87: /* TODO: ESA */

2287 break;

2288 case 0x88: /* HTS -- Horizontal tab stop */

2289 term.tabs[term.c.x] = 1;

2290 break;

2291 case 0x89: /* TODO: HTJ */

2292 case 0x8a: /* TODO: VTS */

2293 case 0x8b: /* TODO: PLD */

2294 case 0x8c: /* TODO: PLU */

2295 case 0x8d: /* TODO: RI */

2296 case 0x8e: /* TODO: SS2 */

2297 case 0x8f: /* TODO: SS3 */

2298 case 0x91: /* TODO: PU1 */

2299 case 0x92: /* TODO: PU2 */

2300 case 0x93: /* TODO: STS */

2301 case 0x94: /* TODO: CCH */

2302 case 0x95: /* TODO: MW */

2303 case 0x96: /* TODO: SPA */

2304 case 0x97: /* TODO: EPA */

2305 case 0x98: /* TODO: SOS */

2306 case 0x99: /* TODO: SGCI */

2307 break;

2308 case 0x9a: /* DECID -- Identify Terminal */

2309 ttywrite(vtiden, strlen(vtiden), 0);

2310 break;

2311 case 0x9b: /* TODO: CSI */

2312 case 0x9c: /* TODO: ST */

2313 break;

2314 case 0x90: /* DCS -- Device Control String */

2315 case 0x9d: /* OSC -- Operating System Command */

2316 case 0x9e: /* PM -- Privacy Message */

2317 case 0x9f: /* APC -- Application Program Command */

2318 tstrsequence(ascii);

2319 return;

2320 }

2321 /* only CAN, SUB, \a and C1 chars interrupt a sequence */

2322 term.esc &= ~(ESC_STR_END|ESC_STR);

2323 }

2324

2325 /*

2326 * returns 1 when the sequence is finished and it hasn't to read

2327 * more characters for this sequence, otherwise 0

2328 */

2329 int

2330 eschandle(uchar ascii)

2331 {

2332 switch (ascii) {

2333 case '[':

2334 term.esc |= ESC_CSI;

2335 return 0;

2336 case '#':

2337 term.esc |= ESC_TEST;

2338 return 0;

2339 case '%':

2340 term.esc |= ESC_UTF8;

2341 return 0;

2342 case 'P': /* DCS -- Device Control String */

2343 case '_': /* APC -- Application Program Command */

2344 case '^': /* PM -- Privacy Message */

2345 case ']': /* OSC -- Operating System Command */

2346 case 'k': /* old title set compatibility */

2347 tstrsequence(ascii);

2348 return 0;

2349 case 'n': /* LS2 -- Locking shift 2 */

2350 case 'o': /* LS3 -- Locking shift 3 */

2351 term.charset = 2 + (ascii - 'n');

2352 break;

2353 case '(': /* GZD4 -- set primary charset G0 */

2354 case ')': /* G1D4 -- set secondary charset G1 */

2355 case '*': /* G2D4 -- set tertiary charset G2 */

2356 case '+': /* G3D4 -- set quaternary charset G3 */

2357 term.icharset = ascii - '(';

2358 term.esc |= ESC_ALTCHARSET;

2359 return 0;

2360 case 'D': /* IND -- Linefeed */

2361 if (term.c.y == term.bot) {

2362 tscrollup(term.top, 1, 1);

2363 } else {

2364 tmoveto(term.c.x, term.c.y+1);

2365 }

2366 break;

2367 case 'E': /* NEL -- Next line */

2368 tnewline(1); /* always go to first col */

2369 break;

2370 case 'H': /* HTS -- Horizontal tab stop */

2371 term.tabs[term.c.x] = 1;

2372 break;

2373 case 'M': /* RI -- Reverse index */

2374 if (term.c.y == term.top) {

2375 tscrolldown(term.top, 1, 1);

2376 } else {

2377 tmoveto(term.c.x, term.c.y-1);

2378 }

2379 break;

2380 case 'Z': /* DECID -- Identify Terminal */

2381 ttywrite(vtiden, strlen(vtiden), 0);

2382 break;

2383 case 'c': /* RIS -- Reset to initial state */

2384 treset();

2385 resettitle();

2386 xloadcols();

2387 break;

2388 case '=': /* DECPAM -- Application keypad */

2389 xsetmode(1, MODE_APPKEYPAD);

2390 break;

2391 case '>': /* DECPNM -- Normal keypad */

2392 xsetmode(0, MODE_APPKEYPAD);

2393 break;

2394 case '7': /* DECSC -- Save Cursor */

2395 tcursor(CURSOR_SAVE);

2396 break;

2397 case '8': /* DECRC -- Restore Cursor */

2398 tcursor(CURSOR_LOAD);

2399 break;

2400 case '\\': /* ST -- String Terminator */

2401 if (term.esc & ESC_STR_END)

2402 strhandle();

2403 break;

2404 default:

2405 #ifdef DEBUG

2406 fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n",

2407 (uchar) ascii, isprint(ascii)? ascii:'.');

2408 #endif

2409 break;

2410 }

2411 return 1;

2412 }

2413

2414 void

2415 tputc(Rune u)

2416 {

2417 char c[UTF_SIZ];

2418 int control;

2419 int width, len;

2420 Glyph *gp;

2421

2422 control = ISCONTROL(u);

2423 if (u < 127 || !IS_SET(MODE_UTF8)) {

2424 c[0] = u;

2425 width = len = 1;

2426 } else {

2427 len = utf8encode(u, c);

2428 if (!control && (width = wcwidth(u)) == -1)

2429 width = 1;

2430 }

2431

2432 if (IS_SET(MODE_PRINT))

2433 tprinter(c, len);

2434

2435 /*

2436 * STR sequence must be checked before anything else

2437 * because it uses all following characters until it

2438 * receives a ESC, a SUB, a ST or any other C1 control

2439 * character.

2440 */

2441 if (term.esc & ESC_STR) {

2442 if (u == '\a' || u == 030 || u == 032 || u == 033 ||

2443 ISCONTROLC1(u)) {

2444 term.esc &= ~(ESC_START|ESC_STR);

2445 term.esc |= ESC_STR_END;

2446 goto check_control_code;

2447 }

2448

2449 if (strescseq.len+len >= strescseq.siz) {

2450 /*

2451 * Here is a bug in terminals. If the user never sends

2452 * some code to stop the str or esc command, then st

2453 * will stop responding. But this is better than

2454 * silently failing with unknown characters. At least

2455 * then users will report back.

2456 *

2457 * In the case users ever get fixed, here is the code:

2458 */

2459 /*

2460 * term.esc = 0;

2461 * strhandle();

2462 */

2463 if (strescseq.siz > (SIZE_MAX - UTF_SIZ) / 2)

2464 return;

2465 strescseq.siz *= 2;

2466 strescseq.buf = xrealloc(strescseq.buf, strescseq.siz);

2467 }

2468

2469 memmove(&strescseq.buf[strescseq.len], c, len);

2470 strescseq.len += len;

2471 return;

2472 }

2473

2474 check_control_code:

2475 /*

2476 * Actions of control codes must be performed as soon they arrive

2477 * because they can be embedded inside a control sequence, and

2478 * they must not cause conflicts with sequences.

2479 */

2480 if (control) {

2481 tcontrolcode(u);

2482 /*

2483 * control codes are not shown ever

2484 */

2485 if (!term.esc)

2486 term.lastc = 0;

2487 return;

2488 } else if (term.esc & ESC_START) {

2489 if (term.esc & ESC_CSI) {

2490 csiescseq.buf[csiescseq.len++] = u;

2491 if (BETWEEN(u, 0x40, 0x7E)

2492 || csiescseq.len >= \

2493 sizeof(csiescseq.buf)-1) {

2494 term.esc = 0;

2495 csiparse();

2496 csihandle();

2497 }

2498 return;

2499 } else if (term.esc & ESC_UTF8) {

2500 tdefutf8(u);

2501 } else if (term.esc & ESC_ALTCHARSET) {

2502 tdeftran(u);

2503 } else if (term.esc & ESC_TEST) {

2504 tdectest(u);

2505 } else {

2506 if (!eschandle(u))

2507 return;

2508 /* sequence already finished */

2509 }

2510 term.esc = 0;

2511 /*

2512 * All characters which form part of a sequence are not

2513 * printed

2514 */

2515 return;

2516 }

2517 if (selected(term.c.x, term.c.y))

2518 selclear();

2519

2520 gp = &term.line[term.c.y][term.c.x];

2521 if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {

2522 gp->mode |= ATTR_WRAP;

2523 tnewline(1);

2524 gp = &term.line[term.c.y][term.c.x];

2525 }

2526

2527 if (IS_SET(MODE_INSERT) && term.c.x+width < term.col)

2528 memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph));

2529

2530 if (term.c.x+width > term.col) {

2531 tnewline(1);

2532 gp = &term.line[term.c.y][term.c.x];

2533 }

2534

2535 tsetchar(u, &term.c.attr, term.c.x, term.c.y);

2536 term.lastc = u;

2537

2538 if (width == 2) {

2539 gp->mode |= ATTR_WIDE;

2540 if (term.c.x+1 < term.col) {

2541 gp[1].u = '\0';

2542 gp[1].mode = ATTR_WDUMMY;

2543 }

2544 }

2545 if (term.c.x+width < term.col) {

2546 tmoveto(term.c.x+width, term.c.y);

2547 } else {

2548 term.c.state |= CURSOR_WRAPNEXT;

2549 }

2550 }

2551

2552 int

2553 twrite(const char *buf, int buflen, int show_ctrl)

2554 {

2555 int charsize;

2556 Rune u;

2557 int n;

2558

2559 for (n = 0; n < buflen; n += charsize) {

2560 if (IS_SET(MODE_UTF8)) {

2561 /* process a complete utf8 char */

2562 charsize = utf8decode(buf + n, &u, buflen - n);

2563 if (charsize == 0)

2564 break;

2565 } else {

2566 u = buf[n] & 0xFF;

2567 charsize = 1;

2568 }

2569 if (show_ctrl && ISCONTROL(u)) {

2570 if (u & 0x80) {

2571 u &= 0x7f;

2572 tputc('^');

2573 tputc('[');

2574 } else if (u != '\n' && u != '\r' && u != '\t') {

2575 u ^= 0x40;

2576 tputc('^');

2577 }

2578 }

2579 tputc(u);

2580 }

2581 return n;

2582 }

2583

2584 void

2585 tresize(int col, int row)

2586 {

2587 int i, j;

2588 int minrow = MIN(row, term.row);

2589 int mincol = MIN(col, term.col);

2590 int *bp;

2591 TCursor c;

2592

2593 if (col < 1 || row < 1) {

2594 #ifdef DEBUG

2595 fprintf(stderr,

2596 "tresize: error resizing to %dx%d\n", col, row);

2597 #endif

2598 return;

2599 }

2600

2601 /*

2602 * slide screen to keep cursor where we expect it -

2603 * tscrollup would work here, but we can optimize to

2604 * memmove because we're freeing the earlier lines

2605 */

2606 for (i = 0; i <= term.c.y - row; i++) {

2607 free(term.line[i]);

2608 free(term.alt[i]);

2609 }

2610 /* ensure that both src and dst are not NULL */

2611 if (i > 0) {

2612 memmove(term.line, term.line + i, row * sizeof(Line));

2613 memmove(term.alt, term.alt + i, row * sizeof(Line));

2614 }

2615 for (i += row; i < term.row; i++) {

2616 free(term.line[i]);

2617 free(term.alt[i]);

2618 }

2619

2620 /* resize to new height */

2621 term.line = xrealloc(term.line, row * sizeof(Line));

2622 term.alt = xrealloc(term.alt, row * sizeof(Line));

2623 term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));

2624 term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));

2625

2626 for (i = 0; i < HISTSIZE; i++) {

2627 term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph));

2628 for (j = mincol; j < col; j++) {

2629 term.hist[i][j] = term.c.attr;

2630 term.hist[i][j].u = ' ';

2631 }

2632 }

2633

2634 /* resize each row to new width, zero-pad if needed */

2635 for (i = 0; i < minrow; i++) {

2636 term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));

2637 term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph));

2638 }

2639

2640 /* allocate any new rows */

2641 for (/* i = minrow */; i < row; i++) {

2642 term.line[i] = xmalloc(col * sizeof(Glyph));

2643 term.alt[i] = xmalloc(col * sizeof(Glyph));

2644 }

2645 if (col > term.col) {

2646 bp = term.tabs + term.col;

2647

2648 memset(bp, 0, sizeof(*term.tabs) * (col - term.col));

2649 while (--bp > term.tabs && !*bp)

2650 /* nothing */ ;

2651 for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)

2652 *bp = 1;

2653 }

2654 /* update terminal size */

2655 term.col = col;

2656 term.row = row;

2657 /* reset scrolling region */

2658 tsetscroll(0, row-1);

2659 /* make use of the LIMIT in tmoveto */

2660 tmoveto(term.c.x, term.c.y);

2661 /* Clearing both screens (it makes dirty all lines) */

2662 c = term.c;

2663 for (i = 0; i < 2; i++) {

2664 if (mincol < col && 0 < minrow) {

2665 tclearregion(mincol, 0, col - 1, minrow - 1);

2666 }

2667 if (0 < col && minrow < row) {

2668 tclearregion(0, minrow, col - 1, row - 1);

2669 }

2670 tswapscreen();

2671 tcursor(CURSOR_LOAD);

2672 }

2673 term.c = c;

2674 }

2675

2676 void

2677 resettitle(void)

2678 {

2679 xsettitle(NULL);

2680 }

2681

2682 void

2683 drawregion(int x1, int y1, int x2, int y2)

2684 {

2685 int y;

2686

2687 for (y = y1; y < y2; y++) {

2688 if (!term.dirty[y])

2689 continue;

2690

2691 term.dirty[y] = 0;

2692 xdrawline(TLINE(y), x1, y, x2);

2693 }

2694 }

2695

2696 void

2697 draw(void)

2698 {

2699 int cx = term.c.x, ocx = term.ocx, ocy = term.ocy;

2700

2701 if (!xstartdraw())

2702 return;

2703

2704 /* adjust cursor position */

2705 LIMIT(term.ocx, 0, term.col-1);

2706 LIMIT(term.ocy, 0, term.row-1);

2707 if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY)

2708 term.ocx--;

2709 if (term.line[term.c.y][cx].mode & ATTR_WDUMMY)

2710 cx--;

2711

2712 drawregion(0, 0, term.col, term.row);

2713 if (term.scr == 0)

2714 xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],

2715 term.ocx, term.ocy, term.line[term.ocy][term.ocx]);

2716 term.ocx = cx;

2717 term.ocy = term.c.y;

2718 xfinishdraw();

2719 if (ocx != term.ocx || ocy != term.ocy)

2720 xximspot(term.ocx, term.ocy);

2721 }

2722

2723 void

2724 redraw(void)

2725 {

2726 tfulldirt();

2727 draw();

2728 }

2729