fuzzy tab completion matching
[vimprobable.git] / utilities.c
blob462a0f06cd724f3e787004bc9119b1fe44348392
1 /*
2 (c) 2009 by Leon Winter
3 (c) 2009-2012 by Hannes Schueller
4 (c) 2009-2010 by Matto Fransen
5 (c) 2010-2011 by Hans-Peter Deifel
6 (c) 2010-2011 by Thomas Adam
7 (c) 2013 Daniel Carl
8 (c) 2015 Matthew Carter
9 see LICENSE file
12 #include "includes.h"
13 #include "vimprobable.h"
14 #include "main.h"
15 #include "utilities.h"
17 extern Client client;
18 extern Command commands[COMMANDSIZE];
19 extern Key keys[];
20 extern gboolean complete_case_sensitive;
21 static GList *dynamic_searchengines = NULL, *dynamic_uri_handlers = NULL;
22 static int command_histsize = 50;
23 int fuzzy_style_match(char *haystack, char *needle);
25 void add_modkeys(char key);
27 static void clear_history(int maxlen)
29 State *s = &client.state;
30 GList *l = s->commandhistory;
31 int len = g_list_length(s->commandhistory);
33 while (l != NULL && len > maxlen) {
34 GList *next = l->next;
35 s->commandhistory = g_list_delete_link(s->commandhistory, l);
36 l = next;
37 len--;
41 void save_command_history(char *line)
43 State *s = &client.state;
44 char *c = line;
46 while (isspace(*c) && *c)
47 c++;
48 if (!strlen(c))
49 return;
51 if (command_histsize > 0) {
52 if (command_histsize <= g_list_length(s->commandhistory)) {
53 clear_history(command_histsize - 1);
55 s->commandhistory = g_list_append(s->commandhistory, g_strdup(c));
59 gboolean clear(const Arg *arg)
61 clear_history(0);
62 return TRUE;
65 void set_command_history_len(int len)
67 command_histsize = len;
68 clear_history(command_histsize);
71 gboolean
72 process_save_qmark(const char *bm, WebKitWebView *webview)
74 FILE *fp;
75 const char *filename;
76 const char *uri = webkit_web_view_get_uri(webview);
77 char qmarks[10][101];
78 char buf[100];
79 int i, mark, l=0;
80 mark = -1;
81 mark = atoi(bm);
82 if ( mark < 1 || mark > 9 ) {
83 echo_message(Error, "Invalid quickmark, only 1-9");
84 return TRUE;
86 if ( uri == NULL ) return FALSE;
87 for( i=0; i < 9; ++i ) strcpy( qmarks[i], "");
89 filename = g_strdup_printf(QUICKMARK_FILE);
91 /* get current quickmarks */
93 fp = fopen(filename, "r");
94 if (fp != NULL){
95 for( i=0; i < 10; ++i ) {
96 if (feof(fp)) {
97 break;
99 fgets(buf, 100, fp);
100 l = 0;
101 while (buf[l] && l < 100 && buf[l] != '\n') {
102 qmarks[i][l]=buf[l];
103 l++;
105 qmarks[i][l]='\0';
107 fclose(fp);
110 /* save quickmarks */
111 strcpy( qmarks[mark-1], uri );
112 fp = fopen(filename, "w");
113 g_free((gpointer *)filename);
114 if (fp == NULL) return FALSE;
115 for( i=0; i < 10; ++i )
116 fprintf(fp, "%s\n", qmarks[i]);
117 fclose(fp);
118 echo_message(Error, "Saved as quickmark %d: %s", mark, uri);
120 return TRUE;
123 void
124 make_keyslist(void)
126 int i;
127 KeyList *ptr, *current;
129 ptr = NULL;
130 current = NULL;
131 i = 0;
132 while ( keys[i].key != 0 )
134 current = malloc(sizeof(KeyList));
135 if (current == NULL) {
136 printf("Not enough memory\n");
137 exit(-1);
139 current->Element = keys[i];
140 current->next = NULL;
141 if (client.config.keylistroot == NULL)
142 client.config.keylistroot = current;
143 if (ptr != NULL)
144 ptr->next = current;
145 ptr = current;
146 i++;
150 gboolean
151 parse_colour(char *color) {
152 char goodcolor[8];
153 int colorlen;
155 colorlen = (int)strlen(color);
157 goodcolor[0] = '#';
158 goodcolor[7] = '\0';
160 /* help the user a bit by making string like
161 #a10 and strings like ffffff full 6digit
162 strings with # in front :)
165 if (color[0] == '#') {
166 switch (colorlen) {
167 case 7:
168 strncpy(goodcolor, color, 7);
169 break;
170 case 4:
171 goodcolor[1] = color[1];
172 goodcolor[2] = color[1];
173 goodcolor[3] = color[2];
174 goodcolor[4] = color[2];
175 goodcolor[5] = color[3];
176 goodcolor[6] = color[3];
177 break;
178 case 2:
179 goodcolor[1] = color[1];
180 goodcolor[2] = color[1];
181 goodcolor[3] = color[1];
182 goodcolor[4] = color[1];
183 goodcolor[5] = color[1];
184 goodcolor[6] = color[1];
185 break;
187 } else {
188 switch (colorlen) {
189 case 6:
190 strncpy(&goodcolor[1], color, 6);
191 break;
192 case 3:
193 goodcolor[1] = color[0];
194 goodcolor[2] = color[0];
195 goodcolor[3] = color[1];
196 goodcolor[4] = color[1];
197 goodcolor[5] = color[2];
198 goodcolor[6] = color[2];
199 break;
200 case 1:
201 goodcolor[1] = color[0];
202 goodcolor[2] = color[0];
203 goodcolor[3] = color[0];
204 goodcolor[4] = color[0];
205 goodcolor[5] = color[0];
206 goodcolor[6] = color[0];
207 break;
211 if (strlen (goodcolor) != 7) {
212 return FALSE;
213 } else {
214 strncpy(color, goodcolor, 8);
215 return TRUE;
219 gboolean
220 process_line_arg(const Arg *arg) {
221 return process_line(arg->s);
224 gboolean
225 changemapping(Key *search_key, int maprecord, char *cmd) {
226 KeyList *current, *newkey, *prev = NULL;
227 Arg a = { .s = cmd };
229 /* sanity check */
230 if (maprecord < 0 && cmd == NULL) {
231 /* possible states:
232 * - maprecord >= 0 && cmd == NULL: mapping to internal symbol
233 * - maprecord < 0 && cmd != NULL: mapping to command line
234 * - maprecord >= 0 && cmd != NULL: cmd will be ignored, treated as mapping to internal symbol
235 * - anything else (gets in here): an error, hence we return FALSE */
236 return FALSE;
239 current = client.config.keylistroot;
241 while (current != NULL) {
242 if (
243 current->Element.mask == search_key->mask &&
244 current->Element.modkey == search_key->modkey &&
245 current->Element.key == search_key->key
247 if (maprecord >= 0) {
248 /* mapping to an internal signal */
249 current->Element.func = commands[maprecord].func;
250 current->Element.arg = commands[maprecord].arg;
251 } else {
252 /* mapping to a command line */
253 current->Element.func = process_line_arg;
254 current->Element.arg = a;
256 return TRUE;
258 prev = current;
259 current = current->next;
262 newkey = malloc(sizeof(KeyList));
263 if (newkey == NULL) {
264 printf("Not enough memory\n");
265 exit (-1);
267 newkey->Element.mask = search_key->mask;
268 newkey->Element.modkey = search_key->modkey;
269 newkey->Element.key = search_key->key;
270 if (maprecord >= 0) {
271 /* mapping to an internal signal */
272 newkey->Element.func = commands[maprecord].func;
273 newkey->Element.arg = commands[maprecord].arg;
274 } else {
275 /* mapping to a command line */
276 newkey->Element.func = process_line_arg;
277 newkey->Element.arg = a;
279 add_modkeys(newkey->Element.modkey);
281 newkey->next = NULL;
283 if (client.config.keylistroot == NULL)
284 client.config.keylistroot = newkey;
286 if (prev != NULL)
287 prev->next = newkey;
289 return TRUE;
292 void add_modkeys(char key)
294 unsigned int k, len;
295 len = strlen(client.config.modkeys );
296 while (k < len ) {
297 if (client.config.modkeys[k] == key ) return;
298 k++;
300 client.config.modkeys = realloc(client.config.modkeys, len + 2);
301 client.config.modkeys[len] = key;
302 client.config.modkeys[len+1] = '\0';
305 gboolean
306 mappings(const Arg *arg) {
307 char line[255];
309 if (!arg->s) {
310 set_error("Missing argument.");
311 return FALSE;
313 strncpy(line, arg->s, 254);
314 if (process_map_line(line))
315 return TRUE;
316 else {
317 set_error("Invalid mapping.");
318 return FALSE;
322 int
323 get_modkey(char key) {
324 switch (key) {
325 case '1':
326 return GDK_MOD1_MASK;
327 case '2':
328 return GDK_MOD2_MASK;
329 case '3':
330 return GDK_MOD3_MASK;
331 case '4':
332 return GDK_MOD4_MASK;
333 case '5':
334 return GDK_MOD5_MASK;
335 default:
336 return FALSE;
340 gboolean
341 process_colon(char *alias, char *cmd) {
342 Alias *a;
344 /* mapping one colon alias to another colon command */
345 a = malloc(sizeof(Alias));
346 if (a == NULL) {
347 fprintf(stderr, "Memory exhausted while saving new command.\n");
348 exit(EXIT_FAILURE);
350 a->alias = malloc(strlen(alias)*sizeof(char));
351 memset(a->alias, 0, strlen(alias));
352 strncpy(a->alias, (alias + 1), strlen(alias) - 1);
353 a->target = cmd;
354 client.config.colon_aliases = g_list_prepend(client.config.colon_aliases, a);
355 return TRUE;
358 gboolean
359 process_mapping(char *keystring, int maprecord, char *cmd) {
360 Key search_key;
362 search_key.mask = 0;
363 search_key.modkey = 0;
364 search_key.key = 0;
366 if (strlen(keystring) == 1) {
367 search_key.key = keystring[0];
370 if (strlen(keystring) == 2) {
371 search_key.modkey= keystring[0];
372 search_key.key = keystring[1];
375 /* process stuff like <S-v> for Shift-v or <C-v> for Ctrl-v (strlen == 5),
376 stuff like <S-v>a for Shift-v,a or <C-v>a for Ctrl-v,a (strlen == 6 && keystring[4] == '>')
377 stuff like <M1-v> for Mod1-v (strlen == 6 && keystring[5] == '>')
378 or stuff like <M1-v>a for Mod1-v,a (strlen = 7)
380 if (
381 ((strlen(keystring) == 5 || strlen(keystring) == 6) && keystring[0] == '<' && keystring[4] == '>') ||
382 ((strlen(keystring) == 6 || strlen(keystring) == 7) && keystring[0] == '<' && keystring[5] == '>')
384 switch (toupper(keystring[1])) {
385 case 'S':
386 search_key.mask = GDK_SHIFT_MASK;
387 if (strlen(keystring) == 5) {
388 keystring[3] = toupper(keystring[3]);
389 } else {
390 keystring[3] = tolower(keystring[3]);
391 keystring[5] = toupper(keystring[5]);
393 break;
394 case 'C':
395 search_key.mask = GDK_CONTROL_MASK;
396 break;
397 case 'M':
398 search_key.mask = get_modkey(keystring[2]);
399 break;
401 if (!search_key.mask)
402 return FALSE;
403 if (strlen(keystring) == 5) {
404 search_key.key = keystring[3];
405 } else if (strlen(keystring) == 7) {
406 search_key.modkey = keystring[4];
407 search_key.key = keystring[6];
408 } else {
409 if (search_key.mask == 'S' || search_key.mask == 'C') {
410 search_key.modkey = keystring[3];
411 search_key.key = keystring[5];
412 } else {
413 search_key.key = keystring[4];
418 /* process stuff like a<S-v> for a,Shift-v or a<C-v> for a,Ctrl-v (strlen == 6)
419 or stuff like a<M1-v> for a,Mod1-v (strlen == 7)
421 if (
422 (strlen(keystring) == 6 && keystring[1] == '<' && keystring[5] == '>') ||
423 (strlen(keystring) == 7 && keystring[1] == '<' && keystring[6] == '>')
425 switch (toupper(keystring[2])) {
426 case 'S':
427 search_key.mask = GDK_SHIFT_MASK;
428 keystring[4] = toupper(keystring[4]);
429 break;
430 case 'C':
431 search_key.mask = GDK_CONTROL_MASK;
432 break;
433 case 'M':
434 search_key.mask = get_modkey(keystring[3]);
435 break;
437 if (!search_key.mask)
438 return FALSE;
439 search_key.modkey= keystring[0];
440 if (strlen(keystring) == 6) {
441 search_key.key = keystring[4];
442 } else {
443 search_key.key = keystring[5];
446 return (changemapping(&search_key, maprecord, cmd));
449 gboolean
450 process_map_line(char *line) {
451 int listlen, i;
452 char *c, *cmd;
453 my_pair.line = line;
454 c = search_word(0);
456 if (!strlen(my_pair.what))
457 return FALSE;
458 while (isspace(*c) && *c)
459 c++;
461 if (*c == ':' || *c == '=')
462 c++;
463 my_pair.line = c;
464 c = search_word(1);
465 if (!strlen(my_pair.value))
466 return FALSE;
467 listlen = LENGTH(commands);
468 for (i = 0; i < listlen; i++) {
469 /* commands is fixed size */
470 if (commands[i].cmd == NULL)
471 break;
472 if (strlen(commands[i].cmd) == strlen(my_pair.value) && strncmp(commands[i].cmd, my_pair.value, strlen(my_pair.value)) == 0) {
473 /* map to an internal symbol */
474 return process_mapping(my_pair.what, i, NULL);
477 /* if this is reached, the mapping is not for one of the internal symbol - test for command line structure */
478 if (strlen(my_pair.value) > 1 && strncmp(my_pair.value, ":", 1) == 0) {
479 /* The string begins with a colon, like a command line, but it's not _just_ a colon,
480 * i.e. increasing the pointer by one will not go 'out of bounds'.
481 * We don't actually check that the command line after the = is valid.
482 * This is user responsibility, the worst case is the new mapping simply doing nothing.
483 * Since we will pass the command to the same function which also handles the config file lines,
484 * we have to strip the colon itself (a colon counts as a commented line there - like in vim).
485 * Last, but not least, the second argument being < 0 signifies to the function that this is a
486 * command line mapping, not a mapping to an existing internal symbol. */
487 cmd = (char *)malloc(sizeof(char) * strlen(my_pair.value));
488 memset(cmd, 0, strlen(my_pair.value));
489 strncpy(cmd, (my_pair.value + 1), strlen(my_pair.value) - 1);
490 if (strncmp(my_pair.what, ":", 1) == 0) {
491 /* map new colon command */
492 return process_colon(my_pair.what, cmd);
493 } else {
494 /* map key binding */
495 return process_mapping(my_pair.what, -1, cmd);
498 return FALSE;
501 gboolean
502 build_taglist(const Arg *arg, FILE *f) {
503 int k = 0, in_tag = 0;
504 int t = 0, marker = 0;
505 char foundtab[MAXTAGSIZE+1];
506 while (arg->s[k]) {
507 if (!isspace(arg->s[k]) && !in_tag) {
508 in_tag = 1;
509 marker = k;
511 if (isspace(arg->s[k]) && in_tag) {
512 /* found a tag */
513 t = 0;
514 while (marker < k && t < MAXTAGSIZE) foundtab[t++] = arg->s[marker++];
515 foundtab[t] = '\0';
516 fprintf(f, " [%s]", foundtab);
517 in_tag = 0;
519 k++;
521 if (in_tag) {
522 t = 0;
523 while (marker < strlen(arg->s) && t < MAXTAGSIZE) foundtab[t++] = arg->s[marker++];
524 foundtab[t] = '\0';
525 fprintf(f, " [%s]", foundtab );
527 return TRUE;
530 void
531 set_error(const char *error) {
532 /* it should never happen that set_error is called more than once,
533 * but to avoid any potential memory leaks, we ignore any subsequent
534 * error if the current one has not been shown */
535 if (client.state.error_msg == NULL) {
536 client.state.error_msg = g_strdup_printf("%s", error);
540 void
541 echo_message(const MessageType type, const char *format, ...)
543 Arg a;
544 va_list ap;
546 va_start(ap, format);
547 a.i = type;
548 a.s = g_strdup_vprintf(format, ap);
549 echo(&a);
550 g_free(a.s);
551 va_end(ap);
555 fuzzy_style_match (char *haystack, char *needle)
557 char *subs;
558 char *next;
559 char word[1000];
560 int iter;
562 iter = 0;
563 subs = needle;
564 next = subs;
566 do {
567 next = strstr(subs + 1 , " ");
568 strcpy(word, subs);
569 word[strlen(subs) - (next != NULL ? strlen(next) : 0)] = '\0';
570 if (strstr(haystack, word + iter) == NULL) {
571 return 0;
573 iter = 1;
574 } while ((subs = strstr(subs + 1, " ")) != NULL);
576 return 1;
579 Listelement *
580 complete_list(const char *searchfor, const int mode, Listelement *elementlist)
582 FILE *f;
583 const char *filename;
584 Listelement *candidatelist = NULL, *candidatepointer = NULL;
585 char s[255] = "", readelement[MAXTAGSIZE + 1] = "";
586 int i, t, n = 0;
588 if (mode == 2) {
589 /* open in history file */
590 filename = g_strdup_printf(HISTORY_STORAGE_FILENAME);
591 } else {
592 /* open in bookmark file (for tags and bookmarks) */
593 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
595 f = fopen(filename, "r");
596 if (f == NULL) {
597 g_free((gpointer *)filename);
598 return (elementlist);
601 while (fgets(s, 254, f)) {
602 if (mode == 1) {
603 /* just tags (could be more than one per line) */
604 i = 0;
605 while (s[i] && i < 254) {
606 while (s[i] != '[' && s[i])
607 i++;
608 if (s[i] != '[')
609 continue;
610 i++;
611 t = 0;
612 while (s[i] != ']' && s[i] && t < MAXTAGSIZE)
613 readelement[t++] = s[i++];
614 readelement[t] = '\0';
615 candidatelist = add_list(readelement, candidatelist);
616 i++;
618 } else {
619 /* complete string (bookmarks & history) */
620 candidatelist = add_list(s, candidatelist);
622 candidatepointer = candidatelist;
623 while (candidatepointer != NULL) {
624 strncpy(s, candidatepointer->element, sizeof(s));
625 if (!complete_case_sensitive) {
626 g_utf8_strdown(s, 255);
628 if (!strlen(searchfor) || fuzzy_style_match(s, (char *) searchfor)) {
629 /* only use string up to the first space */
630 memset(readelement, 0, MAXTAGSIZE + 1);
631 if (strchr(candidatepointer->element, ' ') != NULL) {
632 i = strcspn(candidatepointer->element, " ");
633 if (i > MAXTAGSIZE)
634 i = MAXTAGSIZE;
635 strncpy(readelement, candidatepointer->element, i);
636 } else {
637 strncpy(readelement, candidatepointer->element, MAXTAGSIZE);
639 /* in the case of URLs without title, remove the line break */
640 if (readelement[strlen(readelement) - 1] == '\n') {
641 readelement[strlen(readelement) - 1] = '\0';
643 elementlist = add_list(readelement, elementlist);
644 n = count_list(elementlist);
646 if (n >= MAX_LIST_SIZE)
647 break;
648 candidatepointer = candidatepointer->next;
650 free_list(candidatelist);
651 candidatelist = NULL;
652 if (n >= MAX_LIST_SIZE)
653 break;
655 g_free((gpointer)filename);
656 return (elementlist);
659 Listelement *
660 add_list(const char *element, Listelement *elementlist)
662 int n, i = 0;
663 Listelement *newelement, *elementpointer, *lastelement;
665 if (elementlist == NULL) { /* first element */
666 newelement = malloc(sizeof(Listelement));
667 if (newelement == NULL)
668 return (elementlist);
669 strncpy(newelement->element, element, 254);
670 newelement->next = NULL;
671 return newelement;
673 elementpointer = elementlist;
674 n = strlen(element);
676 /* check if element is already in list */
677 while (elementpointer != NULL) {
678 if (strlen(elementpointer->element) == n &&
679 strncmp(elementpointer->element, element, n) == 0)
680 return (elementlist);
681 lastelement = elementpointer;
682 elementpointer = elementpointer->next;
683 i++;
685 /* add to list */
686 newelement = malloc(sizeof(Listelement));
687 if (newelement == NULL)
688 return (elementlist);
689 lastelement->next = newelement;
690 strncpy(newelement->element, element, 254);
691 newelement->next = NULL;
692 return elementlist;
695 void
696 free_list(Listelement *elementlist)
698 Listelement *elementpointer;
700 while (elementlist != NULL) {
701 elementpointer = elementlist->next;
702 free(elementlist);
703 elementlist = elementpointer;
708 count_list(Listelement *elementlist)
710 Listelement *elementpointer = elementlist;
711 int n = 0;
713 while (elementpointer != NULL) {
714 n++;
715 elementpointer = elementpointer->next;
718 return n;
721 /* split the string at the first occurence of whitespace and return the
722 * position of the second half.
723 * Unlike strtok, the substrings can be empty and the second string is
724 * stripped of trailing and leading whitespace.
725 * Return -1 if `string' contains no whitespace */
726 static int split_string_at_whitespace(char *string)
728 int index = strcspn(string, "\n\t ");
729 if (string[index] != '\0') {
730 string[index++] = 0;
731 g_strstrip(string+index);
732 return index;
734 return -1;
737 /* return TRUE, if the string contains exactly one unescaped %s and no other
738 * printf directives */
739 static gboolean sanity_check_search_url(const char *string)
741 int was_percent_char = 0, percent_s_count = 0;
743 for (; *string; string++) {
744 switch (*string) {
745 case '%':
746 was_percent_char = !was_percent_char;
747 break;
748 case 's':
749 if (was_percent_char)
750 percent_s_count++;
751 was_percent_char = 0;
752 break;
753 default:
754 if (was_percent_char)
755 return FALSE;
756 was_percent_char = 0;
757 break;
761 return percent_s_count == 0 || percent_s_count == 1;
764 void make_searchengines_list(Searchengine *searchengines, int length)
766 int i;
767 for (i = 0; i < length; i++, searchengines++) {
768 dynamic_searchengines = g_list_prepend(dynamic_searchengines, searchengines);
772 /* find a searchengine with a given handle and return its URI or NULL if
773 * nothing is found.
774 * The returned string is internal and must not be freed or modified. */
775 const char *find_uri_for_searchengine(const char *handle)
777 GList *l;
779 if (dynamic_searchengines != NULL) {
780 for (l = dynamic_searchengines; l; l = g_list_next(l)) {
781 Searchengine *s = (Searchengine*)l->data;
782 if (!strcmp(s->handle, handle)) {
783 return s->uri;
788 return NULL;
791 enum ConfigFileError
792 read_rcfile(const char *config)
794 int t, linum = 0, index;
795 char s[255], *buffer;
796 FILE *fpin;
797 gboolean found_malformed_lines = FALSE;
798 Searchengine *new;
799 URIHandler *newhandler;
801 if (access(config, F_OK) != 0)
802 return FILE_NOT_FOUND;
804 fpin = fopen(config, "r");
805 if (fpin == NULL)
806 return READING_FAILED;
808 while (fgets(s, 254, fpin)) {
809 linum++;
811 * ignore lines that begin with #, / and such
813 if (!isalpha(s[0]))
814 continue;
815 t = strlen(s);
816 s[t - 1] = '\0';
817 if (strncmp(s, "shortcut", 8) == 0 || strncmp(s, "searchengine", 12) == 0) {
818 if (strncmp(s, "shortcut", 8) == 0) {
819 buffer = (s + 8);
820 } else {
821 buffer = (s + 12);
823 while (buffer[0] == ' ')
824 buffer++;
825 /* split line at whitespace */
826 index = split_string_at_whitespace(buffer);
827 if (index < 0 || buffer[0] == '\0' || buffer[index] == '\0'
828 || !sanity_check_search_url(buffer+index)) {
829 fprintf(stderr, "searchengines: syntax error on line %d\n", linum);
830 found_malformed_lines = TRUE;
831 continue;
833 new = malloc(sizeof(Searchengine));
834 if (new == NULL) {
835 fprintf(stderr, "Memory exhausted while loading search engines.\n");
836 exit(EXIT_FAILURE);
838 new->handle = g_strdup(buffer);
839 new->uri = g_strdup(buffer+index);
840 dynamic_searchengines = g_list_prepend(dynamic_searchengines, new);
841 } else if (strncmp(s, "handler", 7) == 0) {
842 buffer = (s + 7);
843 while (buffer[0] == ' ')
844 buffer++;
845 /* split line at whitespace */
846 index = split_string_at_whitespace(buffer);
847 if (index < 0 || buffer[0] == '\0' || buffer[index] == '\0'
848 || !sanity_check_search_url(buffer+index)) { /* this sanity check is also valid for handler definitions */
849 fprintf(stderr, "URI handlers: syntax error on line %d\n", linum);
850 found_malformed_lines = TRUE;
851 continue;
853 newhandler = malloc(sizeof(URIHandler));
854 if (newhandler == NULL) {
855 fprintf(stderr, "Memory exhausted while loading uri handlers.\n");
856 exit(EXIT_FAILURE);
858 newhandler->handle = g_strdup(buffer);
859 newhandler->handler = g_strdup(buffer+index);
860 dynamic_uri_handlers = g_list_prepend(dynamic_uri_handlers, newhandler);
861 } else {
862 if (!process_line(s))
863 found_malformed_lines = TRUE;
866 fclose(fpin);
867 return found_malformed_lines ? SYNTAX_ERROR : SUCCESS;
870 void make_uri_handlers_list(URIHandler *uri_handlers, int length)
872 int i;
873 for (i = 0; i < length; i++, uri_handlers++) {
874 dynamic_uri_handlers = g_list_prepend(dynamic_uri_handlers, uri_handlers);
879 /* spawn a child process handling a protocol encoded in URI.
881 On success, pid will contain the pid of the spawned child.
882 If you pass NULL as child_pid, glib will reap the child. */
883 gboolean
884 open_handler_pid(char *uri, GPid *child_pid) {
885 char *argv[64];
886 char *p = NULL, *arg, arg_temp[MAX_SETTING_SIZE], *temp, temp2[MAX_SETTING_SIZE] = "", *temp3;
887 int j;
888 GList *l;
889 GSpawnFlags flags = G_SPAWN_SEARCH_PATH;
891 if (child_pid) {
892 flags |= G_SPAWN_DO_NOT_REAP_CHILD;
894 p = strchr(uri, ':');
895 if (p) {
896 if (dynamic_uri_handlers != NULL) {
897 for (l = dynamic_uri_handlers; l; l = g_list_next(l)) {
898 URIHandler *s = (URIHandler *)l->data;
899 if (strlen(uri) >= strlen(s->handle) && strncmp(s->handle, uri, strlen(s->handle)) == 0) {
900 if (strlen(s->handler) > 0) {
901 arg = (uri + strlen(s->handle));
902 strncpy(temp2, s->handler, MAX_SETTING_SIZE);
903 temp = strtok(temp2, " ");
904 j = 0;
905 while (temp != NULL) {
906 if (strstr(temp, "%s")) {
907 temp3 = temp;
908 memset(arg_temp, 0, MAX_SETTING_SIZE);
909 while (strncmp(temp3, "%s", 2) != 0) {
910 strncat(arg_temp, temp3, 1);
911 temp3++;
913 strcat(arg_temp, arg);
914 temp3++;
915 temp3++;
916 strcat(arg_temp, temp3);
917 argv[j] = arg_temp;
918 } else {
919 argv[j] = temp;
921 temp = strtok(NULL, " ");
922 j++;
924 argv[j] = NULL;
925 g_spawn_async(NULL, argv, NULL, flags,
926 NULL, NULL, child_pid, NULL);
928 return TRUE;
933 return FALSE;
937 gboolean
938 open_handler(char *uri) {
939 return open_handler_pid(uri, NULL);