Used object instead of array to hold data for the hints.
[vimprobable/e.git] / utilities.c
blobba78aec2c8d8ba2a7a20da1386b9a975c469ed86
1 /*
2 (c) 2009 by Leon Winter
3 (c) 2009-2011 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 see LICENSE file
8 */
10 #include "includes.h"
11 #include "vimprobable.h"
12 #include "main.h"
13 #include "utilities.h"
15 extern char commandhistory[COMMANDHISTSIZE][255];
16 extern Command commands[COMMANDSIZE];
17 extern int lastcommand, maxcommands, commandpointer;
18 extern KeyList *keylistroot;
19 extern Key keys[];
20 extern char *error_msg;
21 extern gboolean complete_case_sensitive;
22 static GList *dynamic_searchengines = NULL;
24 gboolean read_rcfile(const char *config)
26 int t;
27 char s[255];
28 FILE *fpin;
29 gboolean returnval = TRUE;
31 if ((fpin = fopen(config, "r")) == NULL)
32 return FALSE;
33 while (fgets(s, 254, fpin)) {
35 * ignore lines that begin with #, / and such
37 if (!isalpha(s[0]))
38 continue;
39 t = strlen(s);
40 s[t - 1] = '\0';
41 if (!process_line(s))
42 returnval = FALSE;
44 fclose(fpin);
45 return returnval;
48 void save_command_history(char *line)
50 char *c;
52 c = line;
53 while (isspace(*c) && *c)
54 c++;
55 if (!strlen(c))
56 return;
57 strncpy(commandhistory[lastcommand], ":", 1);
58 strncat(commandhistory[lastcommand], c, 254);
59 lastcommand++;
60 if (maxcommands < COMMANDHISTSIZE - 1)
61 maxcommands++;
62 if (lastcommand == COMMANDHISTSIZE)
63 lastcommand = 0;
64 commandpointer = lastcommand;
67 gboolean
68 process_save_qmark(const char *bm, WebKitWebView *webview)
70 FILE *fp;
71 const char *filename;
72 const char *uri = webkit_web_view_get_uri(webview);
73 char qmarks[10][101];
74 char buf[100];
75 int i, mark, l=0;
76 Arg a;
77 mark = -1;
78 mark = atoi(bm);
79 if ( mark < 1 || mark > 9 )
81 a.i = Error;
82 a.s = g_strdup_printf("Invalid quickmark, only 1-9");
83 echo(&a);
84 g_free(a.s);
85 return TRUE;
87 if ( uri == NULL ) return FALSE;
88 for( i=0; i < 9; ++i ) strcpy( qmarks[i], "");
90 filename = g_strdup_printf(QUICKMARK_FILE);
92 /* get current quickmarks */
94 fp = fopen(filename, "r");
95 if (fp != NULL){
96 for( i=0; i < 10; ++i ) {
97 if (feof(fp)) {
98 break;
100 fgets(buf, 100, fp);
101 l = 0;
102 while (buf[l] && l < 100 && buf[l] != '\n') {
103 qmarks[i][l]=buf[l];
104 l++;
106 qmarks[i][l]='\0';
108 fclose(fp);
111 /* save quickmarks */
112 strcpy( qmarks[mark-1], uri );
113 fp = fopen(filename, "w");
114 if (fp == NULL) return FALSE;
115 for( i=0; i < 10; ++i )
116 fprintf(fp, "%s\n", qmarks[i]);
117 fclose(fp);
118 a.i = Error;
119 a.s = g_strdup_printf("Saved as quickmark %d: %s", mark, uri);
120 echo(&a);
121 g_free(a.s);
123 return TRUE;
126 void
127 make_keyslist(void)
129 int i;
130 KeyList *ptr, *current;
132 ptr = NULL;
133 current = NULL;
134 i = 0;
135 while ( keys[i].key != 0 )
137 current = malloc(sizeof(KeyList));
138 if (current == NULL) {
139 printf("Not enough memory\n");
140 exit(-1);
142 current->Element = keys[i];
143 current->next = NULL;
144 if (keylistroot == NULL) keylistroot = current;
145 if (ptr != NULL) ptr->next = current;
146 ptr = current;
147 i++;
151 gboolean
152 parse_colour(char *color) {
153 char goodcolor[8];
154 int colorlen;
156 colorlen = (int)strlen(color);
158 goodcolor[0] = '#';
159 goodcolor[7] = '\0';
161 /* help the user a bit by making string like
162 #a10 and strings like ffffff full 6digit
163 strings with # in front :)
166 if (color[0] == '#') {
167 switch (colorlen) {
168 case 7:
169 strncpy(goodcolor, color, 7);
170 break;
171 case 4:
172 goodcolor[1] = color[1];
173 goodcolor[2] = color[1];
174 goodcolor[3] = color[2];
175 goodcolor[4] = color[2];
176 goodcolor[5] = color[3];
177 goodcolor[6] = color[3];
178 break;
179 case 2:
180 goodcolor[1] = color[1];
181 goodcolor[2] = color[1];
182 goodcolor[3] = color[1];
183 goodcolor[4] = color[1];
184 goodcolor[5] = color[1];
185 goodcolor[6] = color[1];
186 break;
188 } else {
189 switch (colorlen) {
190 case 6:
191 strncpy(&goodcolor[1], color, 6);
192 break;
193 case 3:
194 goodcolor[1] = color[0];
195 goodcolor[2] = color[0];
196 goodcolor[3] = color[1];
197 goodcolor[4] = color[1];
198 goodcolor[5] = color[2];
199 goodcolor[6] = color[2];
200 break;
201 case 1:
202 goodcolor[1] = color[0];
203 goodcolor[2] = color[0];
204 goodcolor[3] = color[0];
205 goodcolor[4] = color[0];
206 goodcolor[5] = color[0];
207 goodcolor[6] = color[0];
208 break;
212 if (strlen (goodcolor) != 7) {
213 return FALSE;
214 } else {
215 strncpy(color, goodcolor, 8);
216 return TRUE;
220 gboolean
221 process_line_arg(const Arg *arg) {
222 return process_line(arg->s);
225 gboolean
226 changemapping(Key *search_key, int maprecord, char *cmd) {
227 KeyList *current, *newkey;
228 Arg a = { .s = cmd };
230 /* sanity check */
231 if (maprecord < 0 && cmd == NULL) {
232 /* possible states:
233 * - maprecord >= 0 && cmd == NULL: mapping to internal symbol
234 * - maprecord < 0 && cmd != NULL: mapping to command line
235 * - maprecord >= 0 && cmd != NULL: cmd will be ignored, treated as mapping to internal symbol
236 * - anything else (gets in here): an error, hence we return FALSE */
237 return FALSE;
240 current = keylistroot;
242 if (current)
243 while (current->next != NULL) {
244 if (
245 current->Element.mask == search_key->mask &&
246 current->Element.modkey == search_key->modkey &&
247 current->Element.key == search_key->key
249 if (maprecord >= 0) {
250 /* mapping to an internal signal */
251 current->Element.func = commands[maprecord].func;
252 current->Element.arg = commands[maprecord].arg;
253 } else {
254 /* mapping to a command line */
255 current->Element.func = process_line_arg;
256 current->Element.arg = a;
258 return TRUE;
260 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 newkey->next = NULL;
281 if (keylistroot == NULL) keylistroot = newkey;
283 if (current != NULL) current->next = newkey;
285 return TRUE;
288 gboolean
289 mappings(const Arg *arg) {
290 char line[255];
292 if (!arg->s) {
293 set_error("Missing argument.");
294 return FALSE;
296 strncpy(line, arg->s, 254);
297 if (process_map_line(line))
298 return TRUE;
299 else {
300 set_error("Invalid mapping.");
301 return FALSE;
305 int
306 get_modkey(char key) {
307 switch (key) {
308 case '1':
309 return GDK_MOD1_MASK;
310 case '2':
311 return GDK_MOD2_MASK;
312 case '3':
313 return GDK_MOD3_MASK;
314 case '4':
315 return GDK_MOD4_MASK;
316 case '5':
317 return GDK_MOD5_MASK;
318 default:
319 return FALSE;
323 gboolean
324 process_mapping(char *keystring, int maprecord, char *cmd) {
325 Key search_key;
327 search_key.mask = 0;
328 search_key.modkey = 0;
329 search_key.key = 0;
331 if (strlen(keystring) == 1) {
332 search_key.key = keystring[0];
335 if (strlen(keystring) == 2) {
336 search_key.modkey= keystring[0];
337 search_key.key = keystring[1];
340 /* process stuff like <S-v> for Shift-v or <C-v> for Ctrl-v (strlen == 5),
341 stuff like <S-v>a for Shift-v,a or <C-v>a for Ctrl-v,a (strlen == 6 && keystring[4] == '>')
342 stuff like <M1-v> for Mod1-v (strlen == 6 && keystring[5] == '>')
343 or stuff like <M1-v>a for Mod1-v,a (strlen = 7)
345 if (
346 ((strlen(keystring) == 5 || strlen(keystring) == 6) && keystring[0] == '<' && keystring[4] == '>') ||
347 ((strlen(keystring) == 6 || strlen(keystring) == 7) && keystring[0] == '<' && keystring[5] == '>')
349 switch (toupper(keystring[1])) {
350 case 'S':
351 search_key.mask = GDK_SHIFT_MASK;
352 if (strlen(keystring) == 5) {
353 keystring[3] = toupper(keystring[3]);
354 } else {
355 keystring[3] = tolower(keystring[3]);
356 keystring[5] = toupper(keystring[5]);
358 break;
359 case 'C':
360 search_key.mask = GDK_CONTROL_MASK;
361 break;
362 case 'M':
363 search_key.mask = get_modkey(keystring[2]);
364 break;
366 if (!search_key.mask)
367 return FALSE;
368 if (strlen(keystring) == 5) {
369 search_key.key = keystring[3];
370 } else if (strlen(keystring) == 7) {
371 search_key.modkey = keystring[4];
372 search_key.key = keystring[6];
373 } else {
374 if (search_key.mask == 'S' || search_key.mask == 'C') {
375 search_key.modkey = keystring[3];
376 search_key.key = keystring[5];
377 } else {
378 search_key.key = keystring[4];
383 /* process stuff like a<S-v> for a,Shift-v or a<C-v> for a,Ctrl-v (strlen == 6)
384 or stuff like a<M1-v> for a,Mod1-v (strlen == 7)
386 if (
387 (strlen(keystring) == 6 && keystring[1] == '<' && keystring[5] == '>') ||
388 (strlen(keystring) == 7 && keystring[1] == '<' && keystring[6] == '>')
390 switch (toupper(keystring[2])) {
391 case 'S':
392 search_key.mask = GDK_SHIFT_MASK;
393 keystring[4] = toupper(keystring[4]);
394 break;
395 case 'C':
396 search_key.mask = GDK_CONTROL_MASK;
397 break;
398 case 'M':
399 search_key.mask = get_modkey(keystring[3]);
400 break;
402 if (!search_key.mask)
403 return FALSE;
404 search_key.modkey= keystring[0];
405 if (strlen(keystring) == 6) {
406 search_key.key = keystring[4];
407 } else {
408 search_key.key = keystring[5];
411 return (changemapping(&search_key, maprecord, cmd));
414 gboolean
415 process_map_line(char *line) {
416 int listlen, i;
417 char *c, *cmd;
418 my_pair.line = line;
419 c = search_word(0);
421 if (!strlen(my_pair.what))
422 return FALSE;
423 while (isspace(*c) && *c)
424 c++;
426 if (*c == ':' || *c == '=')
427 c++;
428 my_pair.line = c;
429 c = search_word(1);
430 if (!strlen(my_pair.value))
431 return FALSE;
432 listlen = LENGTH(commands);
433 for (i = 0; i < listlen; i++) {
434 /* commands is fixed size */
435 if (commands[i].cmd == NULL)
436 break;
437 if (strlen(commands[i].cmd) == strlen(my_pair.value) && strncmp(commands[i].cmd, my_pair.value, strlen(my_pair.value)) == 0) {
438 /* map to an internal symbol */
439 return process_mapping(my_pair.what, i, NULL);
442 /* if this is reached, the mapping is not for one of the internal symbol - test for command line structure */
443 if (strlen(my_pair.value) > 1 && strncmp(my_pair.value, ":", 1) == 0) {
444 /* The string begins with a colon, like a command line, but it's not _just_ a colon,
445 * i.e. increasing the pointer by one will not go 'out of bounds'.
446 * We don't actually check that the command line after the = is valid.
447 * This is user responsibility, the worst case is the new mapping simply doing nothing.
448 * Since we will pass the command to the same function which also handles the config file lines,
449 * we have to strip the colon itself (a colon counts as a commented line there - like in vim).
450 * Last, but not least, the second argument being < 0 signifies to the function that this is a
451 * command line mapping, not a mapping to an existing internal symbol. */
452 cmd = (char *)malloc(sizeof(char) * strlen(my_pair.value));
453 strncpy(cmd, (my_pair.value + 1), strlen(my_pair.value) - 1);
454 cmd[strlen(cmd)] = '\0';
455 return process_mapping(my_pair.what, -1, cmd);
457 return FALSE;
460 gboolean
461 build_taglist(const Arg *arg, FILE *f) {
462 int k = 0, in_tag = 0;
463 int t = 0, marker = 0;
464 char foundtab[MAXTAGSIZE+1];
465 while (arg->s[k]) {
466 if (!isspace(arg->s[k]) && !in_tag) {
467 in_tag = 1;
468 marker = k;
470 if (isspace(arg->s[k]) && in_tag) {
471 /* found a tag */
472 t = 0;
473 while (marker < k && t < MAXTAGSIZE) foundtab[t++] = arg->s[marker++];
474 foundtab[t] = '\0';
475 fprintf(f, " [%s]", foundtab);
476 in_tag = 0;
478 k++;
480 if (in_tag) {
481 t = 0;
482 while (marker < strlen(arg->s) && t < MAXTAGSIZE) foundtab[t++] = arg->s[marker++];
483 foundtab[t] = '\0';
484 fprintf(f, " [%s]", foundtab );
486 return TRUE;
489 void
490 set_error(const char *error) {
491 /* it should never happen that set_error is called more than once,
492 * but to avoid any potential memory leaks, we ignore any subsequent
493 * error if the current one has not been shown */
494 if (error_msg == NULL) {
495 error_msg = g_strdup_printf("%s", error);
499 void
500 give_feedback(const char *feedback)
502 Arg a = { .i = Info };
504 a.s = g_strdup_printf("%s", feedback);
505 echo(&a);
506 g_free(a.s);
509 Listelement *
510 complete_list(const char *searchfor, const int mode, Listelement *elementlist)
512 FILE *f;
513 const char *filename;
514 Listelement *candidatelist = NULL, *candidatepointer = NULL;
515 char s[255] = "", readelement[MAXTAGSIZE + 1] = "";
516 int i, t, n = 0;
518 if (mode == 2) {
519 /* open in history file */
520 filename = g_strdup_printf(HISTORY_STORAGE_FILENAME);
521 } else {
522 /* open in bookmark file (for tags and bookmarks) */
523 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
525 f = fopen(filename, "r");
526 if (f == NULL) {
527 g_free((gpointer)filename);
528 return (elementlist);
531 while (fgets(s, 254, f)) {
532 if (mode == 1) {
533 /* just tags (could be more than one per line) */
534 i = 0;
535 while (s[i] && i < 254) {
536 while (s[i] != '[' && s[i])
537 i++;
538 if (s[i] != '[')
539 continue;
540 i++;
541 t = 0;
542 while (s[i] != ']' && s[i] && t < MAXTAGSIZE)
543 readelement[t++] = s[i++];
544 readelement[t] = '\0';
545 candidatelist = add_list(readelement, candidatelist);
546 i++;
548 } else {
549 /* complete string (bookmarks & history) */
550 candidatelist = add_list(s, candidatelist);
552 candidatepointer = candidatelist;
553 while (candidatepointer != NULL) {
554 if (!complete_case_sensitive) {
555 g_strdown(candidatepointer->element);
557 if (!strlen(searchfor) || strstr(candidatepointer->element, searchfor) != NULL) {
558 /* only use string up to the first space */
559 memset(readelement, 0, MAXTAGSIZE + 1);
560 if (strchr(candidatepointer->element, ' ') != NULL) {
561 i = strcspn(candidatepointer->element, " ");
562 strncpy(readelement, candidatepointer->element, i);
563 } else {
564 strncpy(readelement, candidatepointer->element, MAXTAGSIZE);
566 /* in the case of URLs without title, remove the line break */
567 if (readelement[strlen(readelement) - 1] == '\n') {
568 readelement[strlen(readelement) - 1] = '\0';
570 elementlist = add_list(readelement, elementlist);
571 n = count_list(elementlist);
573 if (n >= MAX_LIST_SIZE)
574 break;
575 candidatepointer = candidatepointer->next;
577 free_list(candidatelist);
578 candidatelist = NULL;
579 if (n >= MAX_LIST_SIZE)
580 break;
582 g_free((gpointer)filename);
583 return (elementlist);
586 Listelement *
587 add_list(const char *element, Listelement *elementlist)
589 int n, i = 0;
590 Listelement *newelement, *elementpointer, *lastelement;
592 if (elementlist == NULL) { /* first element */
593 newelement = malloc(sizeof(Listelement));
594 if (newelement == NULL)
595 return (elementlist);
596 strncpy(newelement->element, element, 254);
597 newelement->next = NULL;
598 return newelement;
600 elementpointer = elementlist;
601 n = strlen(element);
603 /* check if element is already in list */
604 while (elementpointer != NULL) {
605 if (strlen(elementpointer->element) == n &&
606 strncmp(elementpointer->element, element, n) == 0)
607 return (elementlist);
608 lastelement = elementpointer;
609 elementpointer = elementpointer->next;
610 i++;
612 /* add to list */
613 newelement = malloc(sizeof(Listelement));
614 if (newelement == NULL)
615 return (elementlist);
616 lastelement->next = newelement;
617 strncpy(newelement->element, element, 254);
618 newelement->next = NULL;
619 return elementlist;
622 void
623 free_list(Listelement *elementlist)
625 Listelement *elementpointer;
627 while (elementlist != NULL) {
628 elementpointer = elementlist->next;
629 free(elementlist);
630 elementlist = elementpointer;
635 count_list(Listelement *elementlist)
637 Listelement *elementpointer = elementlist;
638 int n = 0;
640 while (elementpointer != NULL) {
641 n++;
642 elementpointer = elementpointer->next;
645 return n;
648 /* split the string at the first occurence of whitespace and return the
649 * position of the second half.
650 * Unlike strtok, the substrings can be empty and the second string is
651 * stripped of trailing and leading whitespace.
652 * Return -1 if `string' contains no whitespace */
653 static int split_string_at_whitespace(char *string)
655 int index = strcspn(string, "\n\t ");
656 if (string[index] != '\0') {
657 string[index++] = 0;
658 g_strstrip(string+index);
659 return index;
661 return -1;
664 /* return TRUE, if the string contains exactly one unescaped %s and no other
665 * printf directives */
666 static gboolean sanity_check_search_url(const char *string)
668 int was_percent_char = 0, percent_s_count = 0;
670 for (; *string; string++) {
671 switch (*string) {
672 case '%':
673 was_percent_char = !was_percent_char;
674 break;
675 case 's':
676 if (was_percent_char)
677 percent_s_count++;
678 was_percent_char = 0;
679 break;
680 default:
681 if (was_percent_char)
682 return FALSE;
683 was_percent_char = 0;
684 break;
688 return !was_percent_char && percent_s_count == 1;
691 enum ConfigFileError
692 read_searchengines(const char *filename)
694 FILE *file;
695 char buffer[BUFFERSIZE], c;
696 int linum = 0, index;
697 gboolean found_malformed_lines = FALSE;
698 Searchengine *new;
700 if (access(filename, F_OK) != 0)
701 return FILE_NOT_FOUND;
703 file = fopen(filename, "r");
704 if (file == NULL)
705 return READING_FAILED;
707 while (fgets(buffer, BUFFERSIZE, file)) {
708 linum++;
710 /* skip empty lines */
711 if (!strcmp(buffer, "\n")) continue;
713 /* skip too long lines */
714 if (buffer[strlen(buffer)-1] != '\n') {
715 c = getc(file);
716 if (c != EOF) { /* this is not the last line */
717 while ((c=getc(file)) != EOF && c != '\n');
718 fprintf(stderr, "searchengines: syntax error on line %d\n", linum);
719 found_malformed_lines = TRUE;
720 continue;
724 /* split line at whitespace */
725 index = split_string_at_whitespace(buffer);
727 if (index < 0 || buffer[0] == '\0' || buffer[index] == '\0'
728 || !sanity_check_search_url(buffer+index)) {
729 fprintf(stderr, "searchengines: syntax error on line %d\n", linum);
730 found_malformed_lines = TRUE;
731 continue;
734 new = malloc(sizeof(Searchengine));
735 if (new == NULL) {
736 fprintf(stderr, "Memory exhausted while loading search engines.\n");
737 exit(EXIT_FAILURE);
740 new->handle = g_strdup(buffer);
741 new->uri = g_strdup(buffer+index);
743 dynamic_searchengines = g_list_prepend(dynamic_searchengines, new);
746 if (ferror(file)) {
747 fclose(file);
748 return READING_FAILED;
751 fclose(file);
753 return found_malformed_lines ? SYNTAX_ERROR : SUCCESS;
756 void make_searchengines_list(Searchengine *searchengines, int length)
758 int i;
759 for (i = 0; i < length; i++, searchengines++) {
760 dynamic_searchengines = g_list_prepend(dynamic_searchengines, searchengines);
764 /* find a searchengine with a given handle and return its URI or NULL if
765 * nothing is found.
766 * The returned string is internal and must not be freed or modified. */
767 char *find_uri_for_searchengine(const char *handle)
769 GList *l;
771 if (dynamic_searchengines != NULL) {
772 for (l = dynamic_searchengines; l; l = g_list_next(l)) {
773 Searchengine *s = (Searchengine*)l->data;
774 if (!strcmp(s->handle, handle)) {
775 return s->uri;
780 return NULL;