💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Vgmi › files › c8e215950955db231e7549ac22a87… captured on 2023-11-04 at 12:17:56. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-09-08)

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

Go Back

0 #include <string.h>

1 #include <strings.h>

2 #include <unistd.h>

3 #define TB_IMPL

4 #include "wcwidth.h"

5 #undef wcwidth

6 #define wcwidth(x) mk_wcwidth(x)

7 #ifdef sun

8 #include <termios.h>

9 void cfmakeraw(struct termios *t);

10 #endif

11 #include <termbox.h>

12 #include "gemini.h"

13 #include "display.h"

14 #include "cert.h"

15 #include "str.h"

16 #include "url.h"

17 #include "sandbox.h"

18 #include "util.h"

19 #include <stdio.h>

20

21 int get_cursor_pos() {

22 int pos = 0;

23 for (int i = 0; i < client.input.cursor; i++) {

24 pos += tb_utf8_char_length(client.input.field[pos]);

25 if (!client.input.field[pos]) break;

26 }

27 return pos;

28 }

29

30 int vim_counter() {

31 int counter = atoi(client.vim.counter);

32 bzero(client.vim.counter, sizeof(client.vim.counter));

33 return counter?counter:1;

34 }

35

36 void fix_scroll(struct gmi_tab* tab) {

37 int h = tab->page.lines - tb_height() + 2 + (client.tabs_count > 1);

38 if (tab->scroll > h)

39 tab->scroll = h;

40 if (tab->scroll < 0)

41 tab->scroll = -1;

42 }

43

44 int command() {

45 struct gmi_tab* tab = client.tab;

46 struct gmi_page* page = &tab->page;

47

48 // Trim

49 for (int i = STRNLEN(client.input.field) - 1;

50 client.input.field[i] == ' ' ||

51 client.input.field[i] == '\t'; i--)

52 client.input.field[i] = '\0';

53

54 if (!STRCMP(client.input.field, ":q")) {

55 gmi_freetab(tab);

56 client.input.field[0] = '\0';

57 if (client.tabs_count < 1)

58 return 1;

59 if (client.tabs_count == 1)

60 fix_scroll(client.tab);

61 return 0;

62 }

63 if (!STRCMP(client.input.field, ":qa"))

64 return 1;

65 if ((client.input.field[1] == 'n' && client.input.field[2] == 't'

66 && client.input.field[3] == '\0')

67 || !STRCMP(client.input.field, ":tabnew")) {

68 client.tab = gmi_newtab();

69 client.input.field[0] = '\0';

70 return 0;

71 }

72 if (STARTWITH(client.input.field, ":nt ") ||

73 STARTWITH(client.input.field, ":tabnew ")) {

74 int i = client.input.field[1] == 'n' ? 4 : 8;

75 client.input.cursor = 0;

76 int id = atoi(&client.input.field[i]);

77 if (id != 0 ||

78 (client.input.field[i] == '0' &&

79 client.input.field[i + 1] == '\0')) {

80 gmi_goto_new(tab, id);

81 client.input.field[0] = '\0';

82 } else {

83 client.tab = gmi_newtab_url(&client.input.field[i]);

84 client.input.field[0] = '\0';

85 }

86 return 0;

87 }

88 if (STARTWITH(client.input.field, ":o ")) {

89 char urlbuf[MAX_URL];

90 if (STRLCPY(urlbuf, &client.input.field[3]) >= sizeof(urlbuf)) {

91 tab->show_error = 1;

92 snprintf(tab->error, sizeof(tab->error),

93 "Url too long");

94 return 0;

95 }

96 client.input.field[0] = '\0';

97 gmi_cleanforward(tab);

98 int bytes = gmi_request(tab, urlbuf, 1);

99 if (bytes > 0) {

100 tab->scroll = -1;

101 }

102 if (page->code == 11 || page->code == 10) {

103 client.input.mode = 1;

104 client.input.cursor = 0;

105 }

106 tab->selected = 0;

107 return 0;

108 }

109 if (STARTWITH(client.input.field, ":s")) {

110 char urlbuf[MAX_URL];

111 snprintf(urlbuf, sizeof(urlbuf),

112 "gemini://geminispace.info/search?%s",

113 &client.input.field[3]);

114 client.input.field[0] = '\0';

115 gmi_cleanforward(tab);

116 int bytes = gmi_request(tab, urlbuf, 1);

117 if (bytes > 0) {

118 tab->scroll = -1;

119 }

120 if (page->code == 11 || page->code == 10) {

121 client.input.mode = 1;

122 client.input.cursor = 0;

123 }

124 tab->selected = 0;

125 return 0;

126 }

127 if (STARTWITH(client.input.field, ":add")) {

128 char* title = client.input.field[4] == '\0'?

129 NULL:&client.input.field[5];

130 gmi_addbookmark(tab, tab->url, title);

131 client.input.field[0] = '\0';

132 tab->selected = 0;

133 if (!strcmp("about:home", tab->url)) {

134 gmi_freetab(tab);

135 gmi_gohome(tab, 1);

136 }

137 return 0;

138 }

139 int ignore = STARTWITH(client.input.field, ":ignore");

140 int forget = STARTWITH(client.input.field, ":forget");

141 if (forget || ignore) {

142 char* ptr = client.input.field + sizeof(":forget") - 1;

143 int space = 0;

144 for (; *ptr; ptr++) {

145 if (*ptr != ' ') break;

146 space++;

147 }

148 if (space == 0 || !*ptr) goto unknown;

149 if ((ignore && cert_ignore_expiration(ptr)) ||

150 (forget && cert_forget(ptr))) {

151 tab->show_error = 1;

152 if (forget) {

153 snprintf(tab->error, sizeof(tab->error),

154 "Unknown %s certificate", ptr);

155 } else {

156 strerror_r(errno, tab->error,

157 sizeof(tab->error));

158 }

159 return 0;

160 }

161 tab->show_info = 1;

162 snprintf(tab->info, sizeof(tab->info),

163 "%s %s", ptr, (forget ?

164 "certificate removed from known hosts" :

165 "certificate expiration will be ignored"));

166 client.input.field[0] = '\0';

167 return 0;

168 }

169 if (!STRCMP(client.input.field, ":gencert")) {

170 client.input.field[0] = '\0';

171 tab->selected = 0;

172 if (!STRCMP(tab->url, "about:home")) {

173 tab->show_error = 1;

174 snprintf(tab->error, sizeof(tab->error),

175 "Cannot create a certificate for this page");

176 return 0;

177 }

178 char host[256];

179 parse_url(tab->url, host, sizeof(host), NULL, 0, NULL);

180 if (cert_create(host, tab->error, sizeof(tab->error))) {

181 tab->show_error = 1;

182 return 0;

183 }

184 cert_getcert(host, 1);

185 tab->show_info = 1;

186 snprintf(tab->info, sizeof(tab->info),

187 "Certificate generated for %s", host);

188 return 0;

189 }

190 if (!STRCMP(client.input.field, ":exec")) {

191 #ifdef DISABLE_XDG

192 tab->show_error = 1;

193 snprintf(tab->error, sizeof(tab->error),

194 "xdg is disabled");

195 return 0;

196 #else

197 if (!*client.input.download) {

198 snprintf(tab->error, sizeof(tab->error),

199 "No file was downloaded");

200 tab->show_error = 1;

201 client.input.field[0] = '\0';

202 return 0;

203 }

204 xdg_open(client.input.download);

205 client.input.field[0] = '\0';

206 client.input.download[0] = '\0';

207 return 0;

208 #endif

209 }

210 if (STARTWITH(client.input.field, ":download")) {

211 char* ptr = client.input.field + sizeof(":download") - 1;

212 int space = 0;

213 for (; *ptr; ptr++) {

214 if (*ptr != ' ') break;

215 space++;

216 }

217 if (space == 0 && *ptr) goto unknown;

218 char urlbuf[1024];

219 char* url = strrchr(tab->history->url, '/');

220 if (url && (*(url+1) == '\0')) {

221 while (*url == '/' && url > tab->history->url)

222 url--;

223 char* ptr = url + 1;

224 while (*url != '/' && url > tab->history->url)

225 url--;

226 if (*url == '/') url++;

227 if (strlcpy(urlbuf, url, ptr - url + 1) >=

228 sizeof(urlbuf))

229 return fatalI();

230 url = urlbuf;

231 } else if (url) url++;

232 else url = tab->history->url;

233 char* download = *ptr ? ptr : url;

234 #ifdef SANDBOX_SUN

235 if (sandbox_download(tab, download))

236 return -1;

237 int fd = wr_pair[1];

238 #else

239 int fd = openat(getdownloadfd(), download,

240 O_CREAT|O_EXCL|O_WRONLY, 0600);

241 char buf[1024];

242 if (fd < 0 && errno == EEXIST) {

243 #ifdef __OpenBSD__

244 snprintf(buf, sizeof(buf), "%lld_%s",

245 #else

246 snprintf(buf, sizeof(buf), "%ld_%s",

247 #endif

248 time(NULL), download);

249 fd = openat(getdownloadfd(), buf,

250 O_CREAT|O_EXCL|O_WRONLY, 0600);

251 download = buf;

252 }

253 if (fd < 0) {

254 tab->show_error = -1;

255 snprintf(tab->error, sizeof(tab->error),

256 "Failed to write file : %s", strerror(errno));

257 return 0;

258 }

259 #endif

260 char* data = strnstr(tab->page.data, "\r\n",

261 tab->page.data_len);

262 uint64_t data_len = tab->page.data_len;

263 if (!data) data = tab->page.data;

264 else {

265 data += 2;

266 data_len -= (data - tab->page.data);

267 }

268 #ifdef SANDBOX_SUN

269 sandbox_dl_length(data_len);

270 #endif

271 write(fd, data, data_len);

272 #ifndef SANDBOX_SUN

273 close(fd);

274 #endif

275 tab->show_info = 1;

276 snprintf(tab->info, sizeof(tab->info),

277 "File downloaded : %s", download);

278 STRLCPY(client.input.download, download);

279 client.input.field[0] = '\0';

280 tab->selected = 0;

281 return 0;

282 }

283 if (client.input.field[0] == ':' && (atoi(&client.input.field[1]) ||

284 (client.input.field[1] == '0' && client.input.field[2] == '\0'))) {

285 client.tab->scroll = atoi(&client.input.field[1]) - 1 -

286 !!client.tabs_count;

287 if (client.tab->scroll < 0)

288 client.tab->scroll = -!!client.tabs_count;

289 fix_scroll(client.tab);

290 client.input.field[0] = '\0';

291 tab->selected = 0;

292 return 0;

293 }

294 if (client.input.field[0] == '/') {

295 STRLCPY(tab->search.entry, &client.input.field[1]);

296 client.input.field[0] = '\0';

297 } else if (client.input.field[1] == '\0') client.input.field[0] = '\0';

298 else {

299 unknown:

300 tab->show_error = -1;

301 snprintf(tab->error, sizeof(tab->error),

302 "Unknown input: %s", &client.input.field[1]);

303 }

304 return 0;

305 }

306

307 int input_page(struct tb_event ev) {

308 struct gmi_tab* tab = client.tab;

309 struct gmi_page* page = &tab->page;

310 int counter;

311 switch (ev.key) {

312 case TB_KEY_ESC:

313 tab->selected = 0;

314 bzero(client.vim.counter, sizeof(client.vim.counter));

315 client.vim.g = 0;

316 return 0;

317 case TB_KEY_DELETE:

318 if (strcmp(tab->url, "about:home")) return 0;

319 if (tab->selected && tab->selected > 0 &&

320 tab->selected <= page->links_count) {

321 gmi_removebookmark(tab->selected);

322 tab->selected = 0;

323 gmi_request(tab, tab->url, 0);

324 }

325 return 0;

326 case TB_KEY_BACK_TAB:

327 if (tab->selected && tab->selected > 0 &&

328 tab->selected <= page->links_count) {

329 int linkid = tab->selected;

330 tab->selected = 0;

331 gmi_goto_new(tab, linkid);

332 }

333 return 0;

334 case TB_KEY_ARROW_LEFT:

335 if (ev.mod == TB_MOD_SHIFT)

336 goto go_back;

337 goto tab_prev;

338 case TB_KEY_ARROW_RIGHT:

339 if (ev.mod == TB_MOD_SHIFT)

340 goto go_forward;

341 goto tab_next;

342 case TB_KEY_ARROW_UP:

343 goto move_up;

344 case TB_KEY_ARROW_DOWN:

345 goto move_down;

346 case TB_KEY_TAB:

347 client.vim.g = 0;

348 if (client.vim.counter[0] == '\0' ||

349 !atoi(client.vim.counter)) {

350 if (!tab->selected)

351 return 0;

352 gmi_goto(tab, tab->selected);

353 tab->selected = 0;

354 bzero(client.vim.counter, sizeof(client.vim.counter));

355 return 0;

356 }

357 tab->selected = atoi(client.vim.counter);

358 bzero(client.vim.counter, sizeof(client.vim.counter));

359 if (tab->selected > page->links_count) {

360 snprintf(tab->error, sizeof(tab->error),

361 "Invalid link number");

362 tab->selected = 0;

363 tab->show_error = 1;

364 return 0;

365 }

366 size_t len = STRLCPY(tab->selected_url,

367 page->links[tab->selected - 1]);

368 if (len >= sizeof(tab->selected_url)) {

369 snprintf(tab->error, sizeof(tab->error),

370 "Invalid link, above %lu characters",

371 sizeof(tab->selected_url));

372 tab->selected = 0;

373 tab->show_error = 1;

374 }

375 return 0;

376 case TB_KEY_ENTER:

377 if (client.vim.counter[0] != '\0' &&

378 atoi(client.vim.counter)) {

379 tab->scroll += atoi(client.vim.counter);

380 bzero(client.vim.counter, sizeof(client.vim.counter));

381 }

382 else tab->scroll++;

383 fix_scroll(tab);

384 client.vim.g = 0;

385 return 0;

386 case TB_KEY_PGUP:

387 tab->scroll -= vim_counter() * tb_height() -

388 2 - (client.tabs_count>1);

389 fix_scroll(tab);

390 client.vim.g = 0;

391 return 0;

392 case TB_KEY_PGDN:

393 tab->scroll += vim_counter() * tb_height() -

394 2 - (client.tabs_count>1);

395 fix_scroll(tab);

396 client.vim.g = 0;

397 return 0;

398 }

399 switch (ev.ch) {

400 case 'u':

401 tab->show_error = 0;

402 client.input.mode = 1;

403 snprintf(client.input.field,

404 sizeof(client.input.field),

405 ":o %s", tab->url);

406 client.input.cursor = utf8_len(client.input.field,

407 sizeof(client.input.field));

408 break;

409 case ':':

410 tab->show_error = 0;

411 client.input.mode = 1;

412 client.input.cursor = 1;

413 client.input.field[0] = ':';

414 client.input.field[1] = '\0';

415 break;

416 case '/':

417 tab->show_error = 0;

418 client.input.mode = 1;

419 client.input.cursor = 1;

420 client.input.field[0] = '/';

421 client.input.field[1] = '\0';

422 break;

423 case 'r': // Reload

424 if (!tab->history) break;

425 gmi_request(tab, tab->history->url, 0);

426 break;

427 case 'T': // Tab left

428 tab_prev:

429 if (!client.vim.g) break;

430 client.vim.g = 0;

431 counter = vim_counter();

432 if (!client.tab->next && !client.tab->prev) break;

433 for (int i = counter; i > 0; i--) {

434 if (client.tab->prev)

435 client.tab = client.tab->prev;

436 else

437 while (client.tab->next)

438 client.tab = client.tab->next;

439 fix_scroll(client.tab);

440 }

441 break;

442 case 't': // Tab right

443 tab_next:

444 if (!client.vim.g) break;

445 client.vim.g = 0;

446 counter = vim_counter() % client.tabs_count;

447 if (!client.tab->next && !client.tab->prev) break;

448 for (int i = counter; i > 0; i--) {

449 if (client.tab->next)

450 client.tab = client.tab->next;

451 else

452 while (client.tab->prev)

453 client.tab = client.tab->prev;

454 fix_scroll(client.tab);

455 }

456 break;

457 case 'H': // Back

458 go_back:

459 if (!tab->history || !tab->history->prev) break;

460 tab->selected = 0;

461 if (page->code == 20 || page->code == 10 || page->code == 11) {

462 tab->history->scroll = tab->scroll;

463 if (!tab->history->next) {

464 tab->history->page = tab->page;

465 tab->history->cached = 1;

466 }

467 tab->history = tab->history->prev;

468 tab->scroll = tab->history->scroll;

469 if (tab->history->cached)

470 tab->page = tab->history->page;

471 }

472 if (tab->history->cached) {

473 tab->page = tab->history->page;

474 STRLCPY(tab->url, tab->history->url);

475 } else if (gmi_request(tab, tab->history->url, 1) < 0) break;

476 fix_scroll(client.tab);

477 break;

478 case 'L': // Forward

479 go_forward:

480 if (!tab->history || !tab->history->next) break;

481 tab->selected = 0;

482 if (!tab->history->cached) {

483 tab->history->page = tab->page;

484 tab->history->cached = 1;

485 }

486 if (tab->history->next->cached)

487 tab->page = tab->history->next->page;

488 else if (gmi_request(tab, tab->history->next->url, 1) < 0)

489 break;

490 tab->history->scroll = tab->scroll;

491 tab->history = tab->history->next;

492 tab->scroll = tab->history->scroll;

493 STRLCPY(tab->url, tab->history->url);

494 fix_scroll(client.tab);

495 break;

496 case 'k': // UP

497 move_up:

498 tab->scroll -= vim_counter();

499 fix_scroll(tab);

500 client.vim.g = 0;

501 break;

502 case 'j': // DOWN

503 move_down:

504 tab->scroll += vim_counter();

505 fix_scroll(tab);

506 client.vim.g = 0;

507 break;

508 case 'f': // Show history

509 display_history();

510 break;

511 case 'g': // Start of file

512 if (client.vim.g) {

513 tab->scroll = -1;

514 client.vim.g = 0;

515 } else client.vim.g++;

516 break;

517 case 'G': // End of file

518 tab->scroll = page->lines-tb_height()+2;

519 if (client.tabs_count != 1)

520 tab->scroll++;

521 if (tb_height()-2-(client.tabs_count>1) > page->lines)

522 tab->scroll = -1;

523 break;

524 case 'n': // Next occurence

525 tab->search.cursor++;

526 tab->scroll = tab->search.pos[1] - tb_height()/2;

527 fix_scroll(tab);

528 break;

529 case 'N': // Previous occurence

530 tab->search.cursor--;

531 tab->scroll = tab->search.pos[0] - tb_height()/2;

532 fix_scroll(tab);

533 break;

534 default:

535 if (!(ev.ch >= '0' && ev.ch <= '9'))

536 break;

537 tab->show_error = 0;

538 tab->show_info = 0;

539 unsigned int len = STRNLEN(client.vim.counter);

540 if (len == 0 && ev.ch == '0') break;

541 if (len >= sizeof(client.vim.counter)) break;

542 client.vim.counter[len] = ev.ch;

543 client.vim.counter[len+1] = '\0';

544 }

545 return 0;

546 }

547

548 int input_field(struct tb_event ev) {

549 struct gmi_tab* tab = client.tab;

550 struct gmi_page* page = &tab->page;

551 int pos = 0;

552 switch (ev.key) {

553 case TB_KEY_ESC:

554 client.input.mode = 0;

555 client.input.cursor = 0;

556 if (page->code == 11 || page->code == 10)

557 page->code = 20;

558 client.input.field[0] = '\0';

559 tb_hide_cursor();

560 return 0;

561 case TB_KEY_BACKSPACE:

562 case TB_KEY_BACKSPACE2:

563 if (client.input.cursor >

564 ((page->code == 10 || page->code == 11)?0:1)) {

565 if (client.input.field[0] == '/' &&

566 page->code == 20)

567 tab->search.scroll = 1;

568 char* ptr = client.input.field;

569 int l = 1;

570 int pos = 0;

571 while (*ptr) {

572 l = tb_utf8_char_length(*ptr);

573 pos++;

574 if (pos == client.input.cursor)

575 break;

576 ptr += l;

577 }

578

579 strlcpy(ptr, ptr + l,

580 sizeof(client.input.field) -

581 (ptr - client.input.field));

582 client.input.cursor--;

583 }

584 return 0;

585 case TB_KEY_ENTER:

586 client.input.mode = 0;

587 tb_hide_cursor();

588 if (page->code == 10 || page->code == 11) {

589 char urlbuf[MAX_URL];

590 char* start = strstr(tab->url, "gemini://");

591 char* request = strrchr(tab->url, '?');

592 if (!(start?

593 strchr(&start[GMI], '/'):strchr(tab->url, '/')))

594 snprintf(urlbuf, sizeof(urlbuf),

595 "%s/?%s", tab->url,

596 client.input.field);

597 else if (request && request > strrchr(tab->url, '/')) {

598 *request = '\0';

599 snprintf(urlbuf, sizeof(urlbuf),

600 "%s?%s", tab->url,

601 client.input.field);

602 *request = '?';

603 } else

604 snprintf(urlbuf, sizeof(urlbuf),

605 "%s?%s", tab->url,

606 client.input.field);

607 int bytes = gmi_request(tab, urlbuf, 1);

608 if (bytes>0) {

609 tab = client.tab;

610 tab->scroll = -1;

611 }

612 client.input.field[0] = '\0';

613 return 0;

614 }

615 return command();

616 case TB_KEY_ARROW_LEFT:

617 {

618 int min = 1;

619 if (tab->page.code == 10 || tab->page.code == 11)

620 min = 0;

621 if (ev.mod != TB_MOD_CTRL) {

622 if (client.input.cursor > min)

623 client.input.cursor--;

624 return 0;

625 }

626 pos = get_cursor_pos();

627 while (client.input.cursor > min) {

628 pos -= tb_utf8_char_length(client.input.field[pos]);

629 client.input.cursor--;

630 if (client.input.field[pos] == ' ' ||

631 client.input.field[pos] == '.')

632 break;

633 }

634 return 0;

635 }

636 case TB_KEY_ARROW_RIGHT:

637 pos = get_cursor_pos();

638 if (ev.mod != TB_MOD_CTRL) {

639 if (client.input.field[pos])

640 client.input.cursor++;

641 return 0;

642 }

643 while (client.input.field[pos]) {

644 pos += tb_utf8_char_length(client.input.field[pos]);

645 client.input.cursor++;

646 if (!client.input.field[pos]) break;

647 if (client.input.field[pos] == ' ' ||

648 client.input.field[pos] == '.')

649 break;

650 }

651 return 0;

652 }

653 if (!ev.ch) {

654 tb_hide_cursor();

655 return 0;

656 }

657 char* end = client.input.field;

658 while (*end)

659 end += tb_utf8_char_length(*end);

660 if ((size_t)(end - client.input.field) >=

661 sizeof(client.input.field) - 1)

662 return 0;

663

664 char insert[16];

665 int insert_len = tb_utf8_unicode_to_char(insert, ev.ch);

666 char* start = client.input.field;

667 for (int i = 0; *start && i < client.input.cursor; i++)

668 start += tb_utf8_char_length(*start);

669 for (char* ptr = end; start <= ptr; ptr--)

670 ptr[insert_len] = *ptr;

671

672 memcpy(start, insert, insert_len);

673 client.input.cursor++;

674 end += insert_len;

675 *end = '\0';

676 if (client.input.field[0] != '/' || page->code != 20)

677 return 0;

678 int lines = 0;

679 int posx = 0;

680 int w = tb_width();

681 int found = 0;

682 for (int i = 0; i < tab->page.data_len - 1; i++) {

683 if (posx == 0 && tab->page.data[i] == '=' &&

684 tab->page.data[i + 1] == '>') {

685 int ignore = 0;

686 int firstspace = 0;

687 while (i + ignore < tab->page.data_len) {

688 if (tab->page.data[i + ignore] != '\n') {

689 i += 2 + firstspace;

690 break;

691 }

692 if (tab->page.data[i + ignore] == ' ') {

693 ignore++;

694 if (firstspace) continue;

695 i += ignore;

696 break;

697 }

698 ignore++;

699 firstspace = 1;

700 }

701 if (i > tab->page.data_len)

702 break;

703 }

704 if (lines &&

705 !strncasecmp(&client.input.field[1],

706 &tab->page.data[i],

707 strnlen(&client.input.field[1],

708 sizeof(client.input.field) - 1))) {

709 found = 1;

710 break;

711 }

712 if (posx == w || tab->page.data[i] == '\n') {

713 lines++;

714 posx = 0;

715 continue;

716 }

717 posx++;

718 }

719 if (found) {

720 tab->scroll = lines - tb_height()/2;

721 fix_scroll(tab);

722 }

723 return 0;

724 }

725

726 int input(struct tb_event ev) {

727 struct gmi_tab* tab = client.tab;

728 struct gmi_page* page = &tab->page;

729 if (page->code == 11 || page->code == 10) {

730 if (!client.input.mode) client.input.cursor = 0;

731 client.input.mode = 1;

732 }

733 if (ev.type == TB_EVENT_RESIZE) {

734 fix_scroll(tab);

735 return 0;

736 }

737 if (ev.type != TB_EVENT_KEY) return 0;

738 if (client.input.mode)

739 return input_field(ev);

740 return input_page(ev);

741 }

742

743 int tb_interupt() {

744 int sig = 0;

745 return write(global.resize_pipefd[1], &sig, sizeof(sig)) ==

746 sizeof(sig) ? 0 : -1;

747 }

748