big dialogs: set_curosr2 -> set_dlg_cursor.
[elinks.git] / src / protocol / rewrite / rewrite.c
blob6908c908a3f03794d2f30ae1ac6ad51e743387c1
1 /* URI rewriting module */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include "elinks.h"
9 #include "config/options.h"
10 #include "intl/gettext/libintl.h"
11 #include "main/event.h"
12 #include "main/module.h"
13 #include "protocol/rewrite/rewrite.h"
14 #include "protocol/uri.h"
15 #include "session/location.h"
16 #include "session/session.h"
17 #include "util/string.h"
20 enum uri_rewrite_type {
21 URI_REWRITE_DUMB,
22 URI_REWRITE_SMART,
26 /* TODO: An event hook for follow-url might also feel at home here. --jonas */
28 enum uri_rewrite_option {
29 URI_REWRITE_TREE,
31 URI_REWRITE_ENABLE_DUMB,
32 URI_REWRITE_ENABLE_SMART,
34 URI_REWRITE_DUMB_TREE,
35 URI_REWRITE_DUMB_TEMPLATE,
37 URI_REWRITE_SMART_TREE,
38 URI_REWRITE_SMART_TEMPLATE,
40 URI_REWRITE_OPTIONS,
43 static struct option_info uri_rewrite_options[] = {
44 INIT_OPT_TREE("protocol", N_("URI rewriting"),
45 "rewrite", OPT_SORT,
46 N_("Rules for rewriting URIs entered in the goto dialog.\n"
47 "It makes it possible to define a set of prefixes that will\n"
48 "be expanded if they match a string entered in the goto dialog.\n"
49 "The prefixes can be dumb, meaning that they work only like\n"
50 "URI abbreviations, or smart ones, making it possible to pass\n"
51 "arguments to them like search engine keywords.")),
53 INIT_OPT_BOOL("protocol.rewrite", N_("Enable dumb prefixes"),
54 "enable-dumb", 0, 1,
55 N_("Enable dumb prefixes - simple URI abbreviations which can\n"
56 "be written to the Goto URL dialog instead of actual URIs - i.e.\n"
57 "if you write 'elinks' there, you are directed to\n"
58 "http://elinks.cz/.")),
60 INIT_OPT_BOOL("protocol.rewrite", N_("Enable smart prefixes"),
61 "enable-smart", 0, 1,
62 N_("Enable smart prefixes - URI templates triggered by writing\n"
63 "given abbreviation to the Goto URL dialog followed by a list\n"
64 "of arguments from which the actual URI is composed - i.e.\n"
65 "'gg:search keywords' or 'gn search keywords for news'.")),
67 INIT_OPT_TREE("protocol.rewrite", N_("Dumb Prefixes"),
68 "dumb", OPT_AUTOCREATE | OPT_SORT,
69 N_("Dumb prefixes, see enable-dumb description for details.")),
71 INIT_OPT_STRING("protocol.rewrite.dumb", NULL,
72 "_template_", 0, "",
73 /* xgettext:no-c-format */
74 N_("Replacement URI for this dumbprefix:\n"
75 "%c in the string means the current URL\n"
76 "%% in the string means '%'")),
78 INIT_OPT_TREE("protocol.rewrite", N_("Smart Prefixes"),
79 "smart", OPT_AUTOCREATE | OPT_SORT,
80 N_("Smart prefixes, see enable-smart description for details.")),
82 /* TODO: In some rare occations current link URI and referrer might
83 * also be useful and dare I mention some kind of proxy argument. --jonas */
84 INIT_OPT_STRING("protocol.rewrite.smart", NULL,
85 "_template_", 0, "",
86 /* xgettext:no-c-format */
87 N_("Replacement URI for this smartprefix:\n"
88 "%c in the string means the current URL\n"
89 "%s in the string means the whole argument to smartprefix\n"
90 "%0,%1,...,%9 means argument 0, 1, ..., 9\n"
91 "%% in the string means '%'")),
93 INIT_OPT_STRING("protocol.rewrite", N_("Default template"),
94 "default_template", 0, "",
95 /* xgettext:no-c-format */
96 N_("Default URI template used when the string entered in\n"
97 "the goto dialog does not appear to be a URI or a filename\n"
98 "(i.e. contains no '.', ':' or '/' characters), and does\n"
99 "not match any defined prefixes. Set the value to \"\" to\n"
100 "disable use of the default template rewrite rule.\n"
101 "%c in the template means the current URL,\n"
102 "%s in the template means the whole string from the goto dialog,\n"
103 "%0,%1,...,%9 mean the 1st,2nd,...,10th space-delimited part of %s,\n"
104 "%% in the template means '%'.")),
106 #define INIT_OPT_DUMB_PREFIX(prefix, uri) \
107 INIT_OPT_STRING("protocol.rewrite.dumb", NULL, prefix, 0, uri, NULL)
109 INIT_OPT_DUMB_PREFIX("elinks", ELINKS_WEBSITE_URL),
110 INIT_OPT_DUMB_PREFIX("documentation", ELINKS_DOC_URL),
111 INIT_OPT_DUMB_PREFIX("bz", ELINKS_BUGS_URL),
112 INIT_OPT_DUMB_PREFIX("bug", ELINKS_BUGS_URL),
114 INIT_OPT_DUMB_PREFIX("arc", "http://web.archive.org/web/*/%c"),
115 INIT_OPT_DUMB_PREFIX("cia", "http://cia.navi.cx/"),
116 INIT_OPT_DUMB_PREFIX("b", "http://babelfish.altavista.com/babelfish/tr"),
117 INIT_OPT_DUMB_PREFIX("d", "http://www.dict.org"),
118 INIT_OPT_DUMB_PREFIX("g", "http://www.google.com/"),
119 INIT_OPT_DUMB_PREFIX("gg", "http://www.google.com/"),
120 INIT_OPT_DUMB_PREFIX("go", "http://www.google.com/"),
121 INIT_OPT_DUMB_PREFIX("fm", "http://freshmeat.net/"),
122 INIT_OPT_DUMB_PREFIX("sf", "http://www.sourceforge.net/"),
123 INIT_OPT_DUMB_PREFIX("dbug", "http://bugs.debian.org/"),
124 INIT_OPT_DUMB_PREFIX("dpkg", "http://packages.debian.org/"),
125 /* Hm, is this Debian-centric? -- Miciah */
126 /* Well, does anyone but Debian use lua40 naming convention? --pasky */
127 INIT_OPT_DUMB_PREFIX("lua", "file:///usr/share/doc/lua40-doc/manual/idx.html"),
128 INIT_OPT_DUMB_PREFIX("pycur", "http://www.python.org/doc/current/"),
129 INIT_OPT_DUMB_PREFIX("pydev", "http://www.python.org/dev/doc/devel/"),
130 INIT_OPT_DUMB_PREFIX("pyhelp", "http://starship.python.net/crew/theller/pyhelp.cgi"),
131 INIT_OPT_DUMB_PREFIX("pyvault", "http://www.vex.net/parnassus/"),
132 INIT_OPT_DUMB_PREFIX("e2", "http://www.everything2.org/"),
133 INIT_OPT_DUMB_PREFIX("sd", "http://slashdot.org/"),
134 INIT_OPT_DUMB_PREFIX("vhtml", "http://validator.w3.org/check?uri=%c"),
135 INIT_OPT_DUMB_PREFIX("vcss", "http://jigsaw.w3.org/css-validator/validator?uri=%c"),
137 #define INIT_OPT_SMART_PREFIX(prefix, uri) \
138 INIT_OPT_STRING("protocol.rewrite.smart", NULL, prefix, 0, uri, NULL)
139 #define bugzilla_prefix(prefix) (ELINKS_BUGS_URL prefix)
141 INIT_OPT_SMART_PREFIX("bug", bugzilla_prefix("show_bug.cgi?id=%s")),
143 #ifdef CONFIG_DEBUG
144 INIT_OPT_SMART_PREFIX("milestone-bugs", bugzilla_prefix("buglist.cgi?target_milestone=%s")),
145 INIT_OPT_SMART_PREFIX("search-bugs", bugzilla_prefix("buglist.cgi?short_desc_type=allwordssubstr&short_desc=%s")),
146 #endif
148 INIT_OPT_SMART_PREFIX("arc", "http://web.archive.org/web/*/%s"),
149 INIT_OPT_SMART_PREFIX("bb", "http://babelfish.altavista.com/babelfish/tr?urltext=%s"),
150 INIT_OPT_SMART_PREFIX("bb_fr_en", "http://babelfish.altavista.com/babelfish/tr?lp=fr_en&submit=1&urltext=%s"),
151 INIT_OPT_SMART_PREFIX("bb_en_fr", "http://babelfish.altavista.com/babelfish/tr?lp=en_fr&submit=1&urltext=%s"),
152 INIT_OPT_SMART_PREFIX("cambridge", "http://dictionary.cambridge.org/results.asp?searchword=%s"),
153 INIT_OPT_SMART_PREFIX("cliki", "http://www.cliki.net/admin/search?words=%s"),
154 INIT_OPT_SMART_PREFIX("d", "http://www.dict.org/bin/Dict?Query=%s&Form=Dict1&Strategy=*&Database=*&submit=Submit+query"),
155 INIT_OPT_SMART_PREFIX("dmoz", "http://search.dmoz.org/cgi-bin/search?search=%s"),
156 INIT_OPT_SMART_PREFIX("foldoc", "http://wombat.doc.ic.ac.uk/foldoc/foldoc.cgi?%s"),
157 INIT_OPT_SMART_PREFIX("g", "http://www.google.com/search?q=%s&btnG=Google+Search"),
158 INIT_OPT_SMART_PREFIX("gd", "http://www.google.com/search?q=%s&cat=gwd/Top"),
159 /* Whose idea was it to use 'gg' for websearches? -- Miciah */
160 /* INIT_OPT_SMART_PREFIX("gg", "http://groups.google.com/groups?q=%s"), */
161 INIT_OPT_SMART_PREFIX("gg", "http://www.google.com/search?q=%s&btnG=Google+Search"),
162 INIT_OPT_SMART_PREFIX("gi", "http://images.google.com/images?q=%s"),
163 INIT_OPT_SMART_PREFIX("gn", "http://news.google.com/news?q=%s"),
164 INIT_OPT_SMART_PREFIX("go", "http://www.google.com/search?q=%s&btnG=Google+Search"),
165 INIT_OPT_SMART_PREFIX("gr", "http://groups.google.com/groups?q=%s"),
166 INIT_OPT_SMART_PREFIX("google", "http://www.google.com/search?q=%s"),
167 INIT_OPT_SMART_PREFIX("gwho", "http://www.googlism.com/?ism=%s&name=1"),
168 INIT_OPT_SMART_PREFIX("gwhat", "http://www.googlism.com/?ism=%s&name=2"),
169 INIT_OPT_SMART_PREFIX("gwhere", "http://www.googlism.com/?ism=%s&name=3"),
170 INIT_OPT_SMART_PREFIX("gwhen", "http://www.googlism.com/?ism=%s&name=4"),
171 INIT_OPT_SMART_PREFIX("fm", "http://freshmeat.net/search/?q=%s"),
172 INIT_OPT_SMART_PREFIX("savannah", "http://savannah.nongnu.org/search/?words=%s&type_of_search=soft&exact=1"),
173 INIT_OPT_SMART_PREFIX("sf", "http://sourceforge.net/search/?q=%s"),
174 INIT_OPT_SMART_PREFIX("sfp", "http://sourceforge.net/projects/%s"),
175 INIT_OPT_SMART_PREFIX("sd", "http://slashdot.org/search.pl?query=%s"),
176 INIT_OPT_SMART_PREFIX("sdc", "http://slashdot.org/search.pl?query=%s&op=comments"),
177 INIT_OPT_SMART_PREFIX("sdu", "http://slashdot.org/search.pl?query=%s&op=users"),
178 INIT_OPT_SMART_PREFIX("sdp", "http://slashdot.org/search.pl?query=%s&op=polls"),
179 INIT_OPT_SMART_PREFIX("sdj", "http://slashdot.org/search.pl?query=%s&op=journals"),
180 INIT_OPT_SMART_PREFIX("dbug", "http://bugs.debian.org/%s"),
181 INIT_OPT_SMART_PREFIX("dpkg", "http://packages.debian.org/%s"),
182 INIT_OPT_SMART_PREFIX("emacs", "http://www.emacswiki.org/cgi-bin/wiki.pl?search=%s"),
183 INIT_OPT_SMART_PREFIX("lyrics", "http://music.lycos.com/lyrics/results.asp?QT=L&QW=%s"),
184 INIT_OPT_SMART_PREFIX("lxr", "http://lxr.linux.no/ident?i=%s"),
185 INIT_OPT_SMART_PREFIX("onelook", "http://onelook.com/?w=%s&ls=a"),
186 INIT_OPT_SMART_PREFIX("py", "http://starship.python.net/crew/theller/pyhelp.cgi?keyword=%s&version=current"),
187 INIT_OPT_SMART_PREFIX("pydev", "http://starship.python.net/crew/theller/pyhelp.cgi?keyword=%s&version=devel"),
188 INIT_OPT_SMART_PREFIX("pyvault", "http://py.vaults.ca/apyllo.py?find=%s"),
189 INIT_OPT_SMART_PREFIX("e2", "http://www.everything2.org/?node=%s"),
190 INIT_OPT_SMART_PREFIX("encz", "http://www.slovnik.cz/bin/ecd?ecd_il=1&ecd_vcb=%s&ecd_trn=translate&ecd_trn_dir=0&ecd_lines=15&ecd_hptxt=0"),
191 INIT_OPT_SMART_PREFIX("czen", "http://www.slovnik.cz/bin/ecd?ecd_il=1&ecd_vcb=%s&ecd_trn=translate&ecd_trn_dir=1&ecd_lines=15&ecd_hptxt=0"),
192 INIT_OPT_SMART_PREFIX("dict", "http://dictionary.reference.com/search?q=%s"),
193 INIT_OPT_SMART_PREFIX("thes", "http://thesaurus.reference.com/search?q=%s"),
194 INIT_OPT_SMART_PREFIX("a", "http://acronymfinder.com/af-query.asp?String=exact&Acronym=%s"),
195 INIT_OPT_SMART_PREFIX("imdb", "http://imdb.com/Find?%s"),
196 INIT_OPT_SMART_PREFIX("mw", "http://www.m-w.com/cgi-bin/dictionary?book=Dictionary&va=%s"),
197 INIT_OPT_SMART_PREFIX("mwt", "http://www.m-w.com/cgi-bin/thesaurus?book=Thesaurus&va=%s"),
198 INIT_OPT_SMART_PREFIX("whatis", "http://uptime.netcraft.com/up/graph/?host=%s"),
199 INIT_OPT_SMART_PREFIX("wiki", "http://en.wikipedia.org/w/wiki.phtml?search=%s"),
200 INIT_OPT_SMART_PREFIX("wn", "http://www.cogsci.princeton.edu/cgi-bin/webwn1.7.1?stage=1&word=%s"),
201 /* Search the Free Software Directory */
202 INIT_OPT_SMART_PREFIX("fsd", "http://directory.fsf.org/search/fsd-search.py?q=%s"),
203 /* rfc by number */
204 INIT_OPT_SMART_PREFIX("rfc", "http://www.rfc-editor.org/rfc/rfc%s.txt"),
205 /* rfc search */
206 INIT_OPT_SMART_PREFIX("rfcs", "http://www.rfc-editor.org/cgi-bin/rfcsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"),
207 INIT_OPT_SMART_PREFIX("cr", "http://www.rfc-editor.org/cgi-bin/rfcsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"),
208 /* Internet Draft search */
209 INIT_OPT_SMART_PREFIX("rfcid", "http://www.rfc-editor.org/cgi-bin/idsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"),
210 INIT_OPT_SMART_PREFIX("id", "http://www.rfc-editor.org/cgi-bin/idsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"),
211 INIT_OPT_SMART_PREFIX("draft", "http://www.rfc-editor.org/cgi-bin/idsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"),
213 NULL_OPTION_INFO,
216 #define get_opt_rewrite(which) uri_rewrite_options[(which)].option
217 #define get_dumb_enable() get_opt_rewrite(URI_REWRITE_ENABLE_DUMB).value.number
218 #define get_smart_enable() get_opt_rewrite(URI_REWRITE_ENABLE_SMART).value.number
220 static inline struct option *
221 get_prefix_tree(enum uri_rewrite_option tree)
223 assert(tree == URI_REWRITE_DUMB_TREE
224 || tree == URI_REWRITE_SMART_TREE);
225 return &get_opt_rewrite(tree);
228 #define MAX_URI_ARGS 10
230 static unsigned char *
231 rewrite_uri(unsigned char *url, struct uri *current_uri, unsigned char *arg)
233 struct string n = NULL_STRING;
234 unsigned char *args[MAX_URI_ARGS];
235 int argslen[MAX_URI_ARGS];
236 int argc = 0;
237 int i;
239 if (!init_string(&n)) return NULL;
241 /* Extract space separated list of arguments */
242 args[argc] = arg;
243 for (i = 0; ; i++) {
244 if (args[argc][i] == ' ') {
245 argslen[argc] = i;
246 argc++;
247 if (argc == MAX_URI_ARGS) break;
248 args[argc] = &args[argc - 1][i];
249 i = 0;
250 for (; *args[argc] == ' '; args[argc]++);
251 } else if (!args[argc][i]) {
252 argslen[argc] = i;
253 argc++;
254 break;
258 while (*url) {
259 int p;
260 int value;
262 for (p = 0; url[p] && url[p] != '%'; p++);
264 add_bytes_to_string(&n, url, p);
265 url += p;
267 if (*url != '%') continue;
269 url++;
270 switch (*url) {
271 case 'c':
272 if (!current_uri) break;
273 add_uri_to_string(&n, current_uri, URI_ORIGINAL);
274 break;
275 case 's':
276 if (arg) encode_uri_string(&n, arg, -1, 1);
277 break;
278 case '%':
279 add_char_to_string(&n, '%');
280 break;
281 case '0':
282 case '1':
283 case '2':
284 case '3':
285 case '4':
286 case '5':
287 case '6':
288 case '7':
289 case '8':
290 case '9':
291 value = *url - '0';
292 if (value >= argc) break;
293 encode_uri_string(&n, args[value],
294 argslen[value], 1);
295 break;
296 default:
297 add_bytes_to_string(&n, url - 1, 2);
298 break;
300 if (*url) url++;
303 return n.source;
306 static unsigned char *
307 get_uri_rewrite_prefix(enum uri_rewrite_type type, unsigned char *url)
309 enum uri_rewrite_option tree = type == URI_REWRITE_DUMB
310 ? URI_REWRITE_DUMB_TREE : URI_REWRITE_SMART_TREE;
311 struct option *prefix_tree = get_prefix_tree(tree);
312 struct option *opt = get_opt_rec_real(prefix_tree, url);
313 unsigned char *exp = opt ? opt->value.string : NULL;
315 return (exp && *exp) ? exp : NULL;
318 static enum evhook_status
319 goto_url_hook(va_list ap, void *data)
321 unsigned char **url = va_arg(ap, unsigned char **);
322 struct session *ses = va_arg(ap, struct session *);
323 unsigned char *uu = NULL;
324 unsigned char *arg = "";
325 unsigned char *argstart = *url + strcspn(*url, " :");
327 if (get_smart_enable() && *argstart) {
328 unsigned char bucket = *argstart;
330 *argstart = '\0';
331 uu = get_uri_rewrite_prefix(URI_REWRITE_SMART, *url);
332 *argstart = bucket;
333 arg = argstart + 1;
336 if (get_dumb_enable() && !uu && !*argstart)
337 uu = get_uri_rewrite_prefix(URI_REWRITE_DUMB, *url);
339 if (!uu
340 && !strchr(*url, ':')
341 && !strchr(*url, '.')
342 && !strchr(*url, '/')) {
343 uu = get_opt_str("protocol.rewrite.default_template", NULL);
344 if (uu && *uu) {
345 arg = *url;
346 } else {
347 uu = NULL;
351 if (uu) {
352 struct uri *uri = ses && have_location(ses)
353 ? cur_loc(ses)->vs.uri : NULL;
355 uu = rewrite_uri(uu, uri, arg);
356 if (uu) {
357 mem_free(*url);
358 *url = uu;
362 return EVENT_HOOK_STATUS_NEXT;
365 struct event_hook_info uri_rewrite_hooks[] = {
366 { "goto-url", -1, goto_url_hook },
368 NULL_EVENT_HOOK_INFO
371 struct module uri_rewrite_module = struct_module(
372 /* name: */ N_("URI rewrite"),
373 /* options: */ uri_rewrite_options,
374 /* hooks: */ uri_rewrite_hooks,
375 /* submodules: */ NULL,
376 /* data: */ NULL,
377 /* init: */ NULL,
378 /* done: */ NULL