💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Vgmi › files › 2174e4ab7b18fdf96b407b2d4a4d6… captured on 2022-07-16 at 17:12:27. Gemini links have been rewritten to link to archived content

View Raw

More Information

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

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 #include <termbox.h>

8 #include "gemini.h"

9 #include "display.h"

10 #include "cert.h"

11 #include "str.h"

12 #include <stdio.h>

13

14 void fix_scroll(struct gmi_tab* tab) {

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

16 if (tab->scroll > h)

17 tab->scroll = h;

18 if (tab->scroll < 0)

19 tab->scroll = -1;

20 }

21

22 int command() {

23 struct gmi_tab* tab = &client.tabs[client.tab];

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

25

26 // Trim

27 for (int i=strnlen(client.input.field, sizeof(client.input.field)) - 1;

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

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

30

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

32 client.tabs_count--;

33 gmi_freetab(&client.tabs[client.tab]);

34 for (int i = client.tab; i < client.tabs_count; i++) {

35 client.tabs[i] = client.tabs[i+1];

36 }

37 if (client.tab > 0 && client.tab == client.tabs_count)

38 client.tab--;

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

40 if (client.tabs_count < 1)

41 return 1;

42 if (client.tabs_count == 1)

43 fix_scroll(&client.tabs[0]);

44 return 0;

45 }

46 if (client.input.field[1] == 'q' && client.input.field[2] == 'a'

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

48 return 1;

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

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

51 gmi_newtab();

52 client.tab = client.tabs_count - 1;

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

54 return 0;

55 }

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

57 && client.input.field[3] == ' ') {

58 client.input.cursor = 0;

59 int id = atoi(&client.input.field[4]);

60 if (id != 0 ||

61 (client.input.field[4] == '0' && client.input.field[5] == '\0')) {

62 gmi_goto_new(tab, id);

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

64 } else {

65 gmi_newtab_url(&client.input.field[4]);

66 client.tab = client.tabs_count - 1;

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

68 }

69 return 0;

70 }

71 if (client.input.field[1] == 'o' && client.input.field[2] == ' ') {

72 char urlbuf[MAX_URL];

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

74 tab->show_error = 1;

75 snprintf(tab->error, sizeof(tab->error), "Url too long");

76 return 0;

77 }

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

79 gmi_cleanforward(tab);

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

81 if (bytes > 0) {

82 tab->scroll = -1;

83 }

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

85 client.input.mode = 1;

86 client.input.cursor = 0;

87 }

88 tab->selected = 0;

89 return 0;

90 }

91 if (client.input.field[1] == 's' && client.input.field[2] == ' ') {

92 char urlbuf[MAX_URL];

93 snprintf(urlbuf, sizeof(urlbuf),

94 "gemini://geminispace.info/search?%!s(MISSING)", &client.input.field[3]);

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

96 gmi_cleanforward(tab);

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

98 if (bytes > 0) {

99 tab->scroll = -1;

100 }

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

102 client.input.mode = 1;

103 client.input.cursor = 0;

104 }

105 tab->selected = 0;

106 return 0;

107 }

108 if (client.input.field[1] == 'a' && client.input.field[2] == 'd'

109 && client.input.field[3] == 'd' &&

110 (client.input.field[4] == ' ' || client.input.field[4] == '\0')) {

111 char* title = client.input.field[4] == '\0'?NULL:&client.input.field[5];

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

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

114 tab->selected = 0;

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

116 gmi_freetab(tab);

117 gmi_gohome(tab, 1);

118 }

119 return 0;

120 }

121 if (strncmp(client.input.field, ":forget", sizeof(":forget") - 1) == 0) {

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

123 int space = 0;

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

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

126 space++;

127 }

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

129 if (cert_forget(ptr)) {

130 tab->show_error = 1;

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

132 "Unknown certificate %!s(MISSING)", ptr);

133 return 0;

134 }

135 tab->show_info = 1;

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

137 "%!s(MISSING) certificate removed from known hosts", ptr);

138 return 0;

139 }

140 if (strcmp(client.input.field, ":gencert") == 0) {

141 char host[256];

142 gmi_parseurl(tab->url, host, sizeof(host), NULL, 0, NULL);

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

144 tab->show_error = 1;

145 }

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

147 tab->selected = 0;

148 return 0;

149 }

150 if (strncmp(client.input.field, ":download", sizeof(":download") - 1) == 0) {

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

152 int space = 0;

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

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

155 space++;

156 }

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

158 char urlbuf[1024];

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

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

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

162 url--;

163 char* ptr = url + 1;

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

165 url--;

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

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

168 return fatalI();

169 url = urlbuf;

170 } else if (url) url++;

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

172 int fd = openat(getdownloadfd(), *ptr?ptr:url,

173 O_CREAT|O_EXCL|O_RDWR, 0600);

174 if (fd < 0) {

175 tab->show_error = -1;

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

177 "Failed to write file : %!s(MISSING)", strerror(errno));

178 return 0;

179 }

180 char* data = strnstr(tab->page.data, "\r\n", tab->page.data_len);

181 int data_len = tab->page.data_len;

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

183 else {

184 data += 2;

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

186 }

187 write(fd, data, data_len);

188 close(fd);

189 tab->show_info = 1;

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

191 "File downloaded : %!s(MISSING)", *ptr?ptr:url);

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

193 tab->selected = 0;

194 return 0;

195 }

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

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

198 gmi_goto(tab, atoi(&client.input.field[1]));

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

200 tab->selected = 0;

201 return 0;

202 }

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

204 strlcpy(tab->search.entry, &client.input.field[1],

205 sizeof(tab->search.entry));

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

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

208 else {

209 unknown:

210 tab->show_error = -1;

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

212 "Unknown input: %!s(MISSING)", &client.input.field[1]);

213 }

214 return 0;

215 }

216

217 int input(struct tb_event ev) {

218 struct gmi_tab* tab = &client.tabs[client.tab];

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

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

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

222 client.input.mode = 1;

223 }

224 if (ev.type == TB_EVENT_RESIZE) {

225 fix_scroll(tab);

226 return 0;

227 }

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

229

230 if (!client.input.mode && ev.key == TB_KEY_ESC) {

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

232 client.vim.g = 0;

233 }

234 if (client.input.mode && ev.key == TB_KEY_ESC) {

235 client.input.mode = 0;

236 client.input.cursor = 0;

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

238 page->code = 20;

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

240 tb_hide_cursor();

241 return 0;

242 }

243 if (client.input.mode && (ev.key == TB_KEY_BACKSPACE2 || ev.key == TB_KEY_BACKSPACE)) {

244 int i = client.input.cursor;

245 if (i>((page->code==10||page->code==11)?0:1)) {

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

247 page->code == 20)

248 tab->search.scroll = 1;

249 strlcpy(&client.input.field[i-1],

250 &client.input.field[i], sizeof(client.input.field)-i);

251 client.input.cursor--;

252 }

253 return 0;

254 }

255 if (ev.key == TB_KEY_DELETE) {

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

257 if (tab->selected && tab->selected > 0 && tab->selected <= page->links_count) {

258 gmi_removebookmark(tab->selected);

259 tab->selected = 0;

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

261 }

262 return 0;

263 }

264 if (ev.key == TB_KEY_BACK_TAB) {

265 if (tab->selected && tab->selected > 0 && tab->selected <= page->links_count) {

266 int linkid = tab->selected;

267 tab->selected = 0;

268 gmi_goto_new(tab, linkid);

269 }

270 return 0;

271 }

272 if (!client.input.mode && ev.key == TB_KEY_ARROW_LEFT &&

273 ev.mod == TB_MOD_SHIFT)

274 goto go_back;

275 if (!client.input.mode && ev.key == TB_KEY_ARROW_RIGHT &&

276 ev.mod == TB_MOD_SHIFT)

277 goto go_forward;

278 if (!client.input.mode && ev.key == TB_KEY_ARROW_LEFT)

279 goto tab_prev;

280 if (!client.input.mode && ev.key == TB_KEY_ARROW_RIGHT)

281 goto tab_next;

282 if (!client.input.mode && ev.key == TB_KEY_ARROW_UP)

283 goto move_up;

284 if (!client.input.mode && ev.key == TB_KEY_ARROW_DOWN)

285 goto move_down;

286 if (!client.input.mode && ev.key == TB_KEY_TAB) {

287 if (client.vim.counter[0] != '\0' && atoi(client.vim.counter)) {

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

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

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

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

292 "Invalid link number");

293 tab->selected = 0;

294 tab->show_error = 1;

295 }

296 else if (strlcpy(tab->selected_url, page->links[tab->selected - 1],

297 sizeof(tab->selected_url)) >= sizeof(tab->selected_url)) {

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

299 "Invalid link, above %!l(MISSING)u characters",

300 sizeof(tab->selected_url));

301 tab->selected = 0;

302 tab->show_error = 1;

303 }

304 }

305 else if (tab->selected) {

306 gmi_goto(tab, tab->selected);

307 tab->selected = 0;

308 }

309 client.vim.g = 0;

310 return 0;

311 }

312 if (!client.input.mode && ev.key == TB_KEY_ENTER) {

313 if (client.vim.counter[0] != '\0' && atoi(client.vim.counter)) {

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

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

316 }

317 else tab->scroll++;

318 fix_scroll(tab);

319 client.vim.g = 0;

320 return 0;

321 }

322 if (client.input.mode && ev.key == TB_KEY_ENTER) {

323 client.input.mode = 0;

324 tb_hide_cursor();

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

326 char urlbuf[MAX_URL];

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

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

329 if (!(start?strchr(&start[GMI], '/'):strchr(tab->url, '/')))

330 snprintf(urlbuf, sizeof(urlbuf),

331 "%!s(MISSING)/?%!s(MISSING)", tab->url, client.input.field);

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

333 *request = '\0';

334 snprintf(urlbuf, sizeof(urlbuf),

335 "%!s(MISSING)?%!s(MISSING)", tab->url, client.input.field);

336 *request = '?';

337 } else

338 snprintf(urlbuf, sizeof(urlbuf),

339 "%!s(MISSING)?%!s(MISSING)", tab->url, client.input.field);

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

341 if (bytes>0) {

342 tab = &client.tabs[client.tab];

343 tab->scroll = -1;

344 }

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

346 return 0;

347 }

348 return command();

349 }

350 if (client.input.mode && ev.key == TB_KEY_ARROW_LEFT && ev.mod == TB_MOD_CTRL) {

351 while (client.input.cursor>1) {

352 client.input.cursor--;

353 if (client.input.field[client.input.cursor] == ' ' ||

354 client.input.field[client.input.cursor] == '.')

355 break;

356 }

357 return 0;

358 }

359 if (client.input.mode && ev.key == TB_KEY_ARROW_LEFT) {

360 if (client.input.cursor>1)

361 client.input.cursor--;

362 return 0;

363 }

364 if (client.input.mode && ev.key == TB_KEY_ARROW_RIGHT && ev.mod == TB_MOD_CTRL) {

365 while (client.input.field[client.input.cursor]) {

366 client.input.cursor++;

367 if (client.input.field[client.input.cursor] == ' ' ||

368 client.input.field[client.input.cursor] == '.')

369 break;

370 }

371 return 0;

372 }

373 if (client.input.mode && ev.key == TB_KEY_ARROW_RIGHT) {

374 if (client.input.field[client.input.cursor])

375 client.input.cursor++;

376 return 0;

377 }

378 unsigned int l = strnlen(client.input.field, sizeof(client.input.field));

379 if (client.input.mode && ev.ch && l < sizeof(client.input.field)) {

380 for (int i = l-1; i >= client.input.cursor; i--) {

381 client.input.field[i+1] = client.input.field[i];

382 }

383 client.input.field[client.input.cursor] = ev.ch;

384 client.input.cursor++;

385 l++;

386 client.input.field[l] = '\0';

387 if (client.input.field[0] != '/' ||

388 page->code != 20)

389 return 0;

390 int lines = 0;

391 int posx = 0;

392 int w = tb_width();

393 int found = 0;

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

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

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

397 int ignore = 0;

398 int firstspace = 0;

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

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

401 i += 2 + firstspace;

402 break;

403 }

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

405 ignore++;

406 if (firstspace) continue;

407 i += ignore;

408 break;

409 }

410 ignore++;

411 firstspace = 1;

412 }

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

414 break;

415 }

416 if (lines && !strncasecmp(&client.input.field[1],

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

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

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

420 )) {

421 found = 1;

422 break;

423 }

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

425 lines++;

426 posx=0;

427 continue;

428 }

429 posx++;

430 }

431 if (found) {

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

433 fix_scroll(tab);

434 }

435 return 0;

436 } else

437 tb_hide_cursor();

438

439 if (ev.key == TB_KEY_PGUP) {

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

441 if (!counter) counter++;

442 int H = tb_height() - 2 - (client.tabs_count>1);

443 tab->scroll -= counter * H;

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

445 fix_scroll(tab);

446 client.vim.g = 0;

447 return 0;

448 }

449 if (ev.key == TB_KEY_PGDN) {

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

451 if (!counter) counter++;

452 int H = tb_height() - 2 - (client.tabs_count>1);

453 tab->scroll += counter * H;

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

455 fix_scroll(tab);

456 client.vim.g = 0;

457 return 0;

458 }

459 switch (ev.ch) {

460 case 'u':

461 tab->show_error = 0;

462 client.input.mode = 1;

463 snprintf(client.input.field, sizeof(client.input.field), ":o %!s(MISSING)", tab->url);

464 client.input.cursor = strnlen(client.input.field, sizeof(client.input.field));

465 break;

466 case ':':

467 tab->show_error = 0;

468 client.input.mode = 1;

469 client.input.cursor = 1;

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

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

472 break;

473 case '/':

474 tab->show_error = 0;

475 client.input.mode = 1;

476 client.input.cursor = 1;

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

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

479 break;

480 case 'r': // Reload

481 if (!tab->history) break;

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

483 break;

484 case 'h': // Tab left

485 tab_prev:

486 client.tab--;

487 if (client.tab < 0) client.tab = client.tabs_count - 1;

488 break;

489 case 'l': // Tab right

490 tab_next:

491 client.tab++;

492 if (client.tab >= client.tabs_count) client.tab = 0;

493 break;

494 case 'H': // Back

495 go_back:

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

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

498 tab->history->scroll = tab->scroll;

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

500 tab->history->page = tab->page;

501 tab->history->cached = 1;

502 }

503 tab->history = tab->history->prev;

504 tab->scroll = tab->history->scroll;

505 if (tab->history->cached)

506 tab->page = tab->history->page;

507 }

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

509 tab->page = tab->history->page;

510 strlcpy(tab->url, tab->history->url, sizeof(tab->url));

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

512 break;

513 case 'L': // Forward

514 go_forward:

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

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

517 tab->history->page = tab->page;

518 tab->history->cached = 1;

519 }

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

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

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

523 tab->history->scroll = tab->scroll;

524 tab->history = tab->history->next;

525 tab->scroll = tab->history->scroll;

526 strlcpy(tab->url, tab->history->url, sizeof(tab->url));

527 break;

528 case 'k': // UP

529 move_up:

530 if (client.vim.counter[0] != '\0' && atoi(client.vim.counter)) {

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

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

533 }

534 else tab->scroll--;

535 fix_scroll(tab);

536 client.vim.g = 0;

537 break;

538 case 'j': // DOWN

539 move_down:

540 if (client.vim.counter[0] != '\0' && atoi(client.vim.counter)) {

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

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

543 }

544 else tab->scroll++;

545 fix_scroll(tab);

546 client.vim.g = 0;

547 break;

548 case 'f': // Show history

549 display_history();

550 break;

551 case 'g': // Start of file

552 if (client.vim.g) {

553 tab->scroll = -1;

554 client.vim.g = 0;

555 } else client.vim.g++;

556 break;

557 case 'G': // End of file

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

559 if (client.tabs_count != 1) tab->scroll++;

560 if (tb_height()-2-(client.tabs_count>1) > page->lines) tab->scroll = -1;

561 break;

562 case 'n': // Next occurence

563 tab->search.cursor++;

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

565 fix_scroll(tab);

566 break;

567 case 'N': // Previous occurence

568 tab->search.cursor--;

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

570 fix_scroll(tab);

571 break;

572 default:

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

574 break;

575 tab->show_error = 0;

576 tab->show_info = 0;

577 unsigned int len = strnlen(client.vim.counter, sizeof(client.vim.counter));

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

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

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

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

582 }

583 return 0;

584 }

585

586 int tb_interupt() {

587 int sig = 0;

588 return write(global.resize_pipefd[1], &sig, sizeof(sig))==sizeof(sig)?0:-1;

589 }

590