💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Vgmi › files › b4b1a06acf1bc909e364cfab429eb… captured on 2023-12-28 at 15:43:24. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
0 /*
1 * ISC License
2 * Copyright (c) 2023 RMF <rawmonk@firemail.cc>
3 */
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <stdint.h>
8 #include "macro.h"
9 #include "strlcpy.h"
10 #include "utf8.h"
11 #include "termbox.h"
12 #include "gemini.h"
13 #include "page.h"
14 #include "request.h"
15 #include "client.h"
16 #include "commands.h"
17 #include "tab.h"
18 #include "error.h"
19 #include "parser.h"
20
21 void client_enter_mode_cmdline(struct client *client) {
22 client->count = 0;
23 client->error = 0;
24 memset(client->cmd, 0, sizeof(client->cmd));
25 client->mode = MODE_CMDLINE;
26 client->cursor = STRLCPY(client->cmd, ":");
27 }
28
29 static void refresh_search(struct client *client) {
30 struct request *req;
31 if (client->cmd[0] != '/') return;
32 STRLCPY(client->search, &client->cmd[1]);
33 if (!(req = tab_completed(client->tab))) return;
34 page_search(&req->page, client->search);
35 req->scroll = page_selection_line(req->page);
36 req->scroll -= client_display_rect(client).h / 2;
37 tab_scroll(client->tab, 0, client_display_rect(client));
38 }
39
40 int handle_cmd(struct client *client) {
41
42 const char invalid[] = "Invalid command: %s";
43 char name[MAX_CMD_NAME], cmd[MAX_CMDLINE - sizeof(invalid)];
44 size_t i;
45 int number;
46
47 ASSERT(sizeof(cmd) > sizeof(name))
48
49 STRLCPY(cmd, &client->cmd[1]);
50
51 number = atoi(cmd);
52 if (number || !STRCMP(cmd, "0")) {
53 struct request *req;
54 if (!client->tab) return 0;
55 req = tab_completed(client->tab);
56 if (!req) return 0;
57 req->scroll = number;
58 request_scroll(req, 0, client_display_rect(client));
59 return 0;
60 }
61
62 for (i = 0; i < sizeof(name); ) {
63 char c = cmd[i];
64 size_t len;
65 if (c == ' ' || c == '\t' || c == '\0') {
66 name[i] = '\0';
67 break;
68 }
69 len = i + utf8_char_length(cmd[i]);
70 for (; i < len; i++)
71 name[i] = cmd[i];
72 }
73 for (i = 0; i < LENGTH(commands); i++) {
74 int j;
75 if (STRCMP(commands[i].name, name)) continue;
76 j = strnlen(V(commands[i].name));
77 for (; cmd[j] && WHITESPACE(cmd[j]); j++) ;
78 if (commands[i].command(client, &cmd[j], sizeof(cmd) - j))
79 return 1;
80 return 0;
81 }
82 client->error = 1;
83 snprintf(V(client->cmd), invalid, cmd);
84 return 0;
85 }
86
87 int client_input_cmdline(struct client *client, struct tb_event ev) {
88
89 int len, prefix;
90 struct request *req;
91 int search_mode = client->cmd[0] == '/';
92
93 req = tab_input(client->tab);
94
95 if (req) {
96 prefix = strnlen(V(req->meta)) + 2;
97 if (client->cursor < prefix) client->cursor = prefix;
98 } else prefix = 1;
99
100 switch (ev.key) {
101 case TB_KEY_ESC:
102 client->mode = MODE_NORMAL;
103 if (req) req->state = STATE_ENDED;
104 return 0;
105 case TB_KEY_ENTER:
106 if (search_mode) ;
107 else if (req) {
108 char url[2048];
109 int error;
110 req->state = STATE_ENDED;
111 len = snprintf(V(url), "%s?%s", req->url,
112 client->tab->input);
113 error = len >= MAX_URL ?
114 ERROR_URL_TOO_LONG :
115 tab_request(client->tab, url);
116 if (error) {
117 client->tab->failure = 1;
118 error_string(error, V(client->tab->error));
119 }
120 } else if (handle_cmd(client)) return 1;
121 client->mode = MODE_NORMAL;
122 return 0;
123 case TB_KEY_BACKSPACE:
124 case TB_KEY_BACKSPACE2:
125 if (client->cursor <= prefix) {
126 if (!req) client->mode = MODE_NORMAL;
127 return 0;
128 }
129 if (!req) {
130 client->cursor = utf8_previous(client->cmd,
131 client->cursor);
132 client->cmd[client->cursor] = 0;
133 refresh_search(client);
134 return 0;
135 }
136 client->cursor = utf8_previous(client->tab->input,
137 client->cursor - prefix) + prefix;
138 client->tab->input[client->cursor - prefix] = 0;
139 goto rewrite;
140 }
141
142 if (!ev.ch) return 0;
143
144 len = utf8_unicode_length(ev.ch);
145 if ((size_t)(client->cursor + len) >= sizeof(client->cmd)) return 0;
146
147 if (!req) {
148 len = utf8_unicode_to_char(
149 &client->cmd[client->cursor], ev.ch);
150 client->cursor += len;
151 client->cmd[client->cursor] = '\0';
152 refresh_search(client);
153 return 0;
154 }
155
156 len = utf8_unicode_to_char(
157 &client->tab->input[client->cursor - prefix], ev.ch);
158 client->cursor += len;
159 client->tab->input[client->cursor - prefix] = '\0';
160 rewrite:
161 if (req->status == GMI_INPUT) {
162 strlcpy(&client->cmd[prefix], client->tab->input,
163 sizeof(client->cmd) - prefix);
164 } else if (req->status == GMI_SECRET) {
165 int i = 0;
166 size_t j;
167 for (j = prefix; j < sizeof(client->cmd) &&
168 client->tab->input[i]; j++) {
169 i += utf8_char_length(client->tab->input[i]);
170 client->cmd[j] = '*';
171 }
172 client->cmd[j] = '\0';
173 }
174
175 return 0;
176 }
177