Use if-else-if in process_set_line().
[vimprobable2.git] / utilities.c
blobe3dd0f6ec3e0e675ff76b3f3670a09afe4274f61
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 extern char *config_base;
23 static GList *dynamic_searchengines = NULL;
25 void add_modkeys(char key);
27 gboolean read_rcfile(const char *config)
29 int t;
30 char s[255];
31 FILE *fpin;
32 gboolean returnval = TRUE;
34 if ((fpin = fopen(config, "r")) == NULL)
35 return FALSE;
36 while (fgets(s, 254, fpin)) {
38 * ignore lines that begin with #, / and such
40 if (!isalpha(s[0]))
41 continue;
42 t = strlen(s);
43 s[t - 1] = '\0';
44 if (!process_line(s))
45 returnval = FALSE;
47 fclose(fpin);
48 return returnval;
51 void save_command_history(char *line)
53 char *c;
55 c = line;
56 while (isspace(*c) && *c)
57 c++;
58 if (!strlen(c))
59 return;
60 strncpy(commandhistory[lastcommand], ":", 1);
61 strncat(commandhistory[lastcommand], c, 254);
62 lastcommand++;
63 if (maxcommands < COMMANDHISTSIZE - 1)
64 maxcommands++;
65 if (lastcommand == COMMANDHISTSIZE)
66 lastcommand = 0;
67 commandpointer = lastcommand;
70 gboolean
71 process_save_qmark(const char *bm, WebKitWebView *webview)
73 FILE *fp;
74 const char *filename;
75 const char *uri = webkit_web_view_get_uri(webview);
76 char qmarks[10][101];
77 char buf[100];
78 int i, mark, l=0;
79 Arg a;
80 mark = -1;
81 mark = atoi(bm);
82 if ( mark < 1 || mark > 9 )
84 a.i = Error;
85 a.s = g_strdup_printf("Invalid quickmark, only 1-9");
86 echo(&a);
87 g_free(a.s);
88 return TRUE;
90 if ( uri == NULL ) return FALSE;
91 for( i=0; i < 9; ++i ) strcpy( qmarks[i], "");
93 filename = g_strdup_printf(QUICKMARK_FILE);
95 /* get current quickmarks */
97 fp = fopen(filename, "r");
98 if (fp != NULL){
99 for( i=0; i < 10; ++i ) {
100 if (feof(fp)) {
101 break;
103 fgets(buf, 100, fp);
104 l = 0;
105 while (buf[l] && l < 100 && buf[l] != '\n') {
106 qmarks[i][l]=buf[l];
107 l++;
109 qmarks[i][l]='\0';
111 fclose(fp);
114 /* save quickmarks */
115 strcpy( qmarks[mark-1], uri );
116 fp = fopen(filename, "w");
117 g_free((gpointer *)filename);
118 if (fp == NULL) return FALSE;
119 for( i=0; i < 10; ++i )
120 fprintf(fp, "%s\n", qmarks[i]);
121 fclose(fp);
122 a.i = Error;
123 a.s = g_strdup_printf("Saved as quickmark %d: %s", mark, uri);
124 echo(&a);
125 g_free(a.s);
127 return TRUE;
130 void
131 make_keyslist(void)
133 int i;
134 KeyList *ptr, *current;
136 ptr = NULL;
137 current = NULL;
138 i = 0;
139 while ( keys[i].key != 0 )
141 current = malloc(sizeof(KeyList));
142 if (current == NULL) {
143 printf("Not enough memory\n");
144 exit(-1);
146 current->Element = keys[i];
147 current->next = NULL;
148 if (keylistroot == NULL) keylistroot = current;
149 if (ptr != NULL) ptr->next = current;
150 ptr = current;
151 i++;
155 gboolean
156 parse_colour(char *color) {
157 char goodcolor[8];
158 int colorlen;
160 colorlen = (int)strlen(color);
162 goodcolor[0] = '#';
163 goodcolor[7] = '\0';
165 /* help the user a bit by making string like
166 #a10 and strings like ffffff full 6digit
167 strings with # in front :)
170 if (color[0] == '#') {
171 switch (colorlen) {
172 case 7:
173 strncpy(goodcolor, color, 7);
174 break;
175 case 4:
176 goodcolor[1] = color[1];
177 goodcolor[2] = color[1];
178 goodcolor[3] = color[2];
179 goodcolor[4] = color[2];
180 goodcolor[5] = color[3];
181 goodcolor[6] = color[3];
182 break;
183 case 2:
184 goodcolor[1] = color[1];
185 goodcolor[2] = color[1];
186 goodcolor[3] = color[1];
187 goodcolor[4] = color[1];
188 goodcolor[5] = color[1];
189 goodcolor[6] = color[1];
190 break;
192 } else {
193 switch (colorlen) {
194 case 6:
195 strncpy(&goodcolor[1], color, 6);
196 break;
197 case 3:
198 goodcolor[1] = color[0];
199 goodcolor[2] = color[0];
200 goodcolor[3] = color[1];
201 goodcolor[4] = color[1];
202 goodcolor[5] = color[2];
203 goodcolor[6] = color[2];
204 break;
205 case 1:
206 goodcolor[1] = color[0];
207 goodcolor[2] = color[0];
208 goodcolor[3] = color[0];
209 goodcolor[4] = color[0];
210 goodcolor[5] = color[0];
211 goodcolor[6] = color[0];
212 break;
216 if (strlen (goodcolor) != 7) {
217 return FALSE;
218 } else {
219 strncpy(color, goodcolor, 8);
220 return TRUE;
224 gboolean
225 process_line_arg(const Arg *arg) {
226 return process_line(arg->s);
229 gboolean
230 changemapping(Key *search_key, int maprecord, char *cmd) {
231 KeyList *current, *newkey;
232 Arg a = { .s = cmd };
234 /* sanity check */
235 if (maprecord < 0 && cmd == NULL) {
236 /* possible states:
237 * - maprecord >= 0 && cmd == NULL: mapping to internal symbol
238 * - maprecord < 0 && cmd != NULL: mapping to command line
239 * - maprecord >= 0 && cmd != NULL: cmd will be ignored, treated as mapping to internal symbol
240 * - anything else (gets in here): an error, hence we return FALSE */
241 return FALSE;
244 current = keylistroot;
246 if (current)
247 while (current->next != NULL) {
248 if (
249 current->Element.mask == search_key->mask &&
250 current->Element.modkey == search_key->modkey &&
251 current->Element.key == search_key->key
253 if (maprecord >= 0) {
254 /* mapping to an internal signal */
255 current->Element.func = commands[maprecord].func;
256 current->Element.arg = commands[maprecord].arg;
257 } else {
258 /* mapping to a command line */
259 current->Element.func = process_line_arg;
260 current->Element.arg = a;
262 return TRUE;
264 current = current->next;
266 newkey = malloc(sizeof(KeyList));
267 if (newkey == NULL) {
268 printf("Not enough memory\n");
269 exit (-1);
271 newkey->Element.mask = search_key->mask;
272 newkey->Element.modkey = search_key->modkey;
273 newkey->Element.key = search_key->key;
274 if (maprecord >= 0) {
275 /* mapping to an internal signal */
276 newkey->Element.func = commands[maprecord].func;
277 newkey->Element.arg = commands[maprecord].arg;
278 } else {
279 /* mapping to a command line */
280 newkey->Element.func = process_line_arg;
281 newkey->Element.arg = a;
283 add_modkeys(newkey->Element.modkey);
285 newkey->next = NULL;
287 if (keylistroot == NULL) keylistroot = newkey;
289 if (current != NULL) current->next = newkey;
291 return TRUE;
294 void add_modkeys(char key)
296 unsigned int k, len;
297 extern char *modkeys;
298 len = strlen( modkeys );
299 while (k < len )
301 if ( modkeys[k] == key ) return;
302 k++;
304 modkeys = realloc(modkeys, len + 1);
305 modkeys[len++] = key;
306 modkeys[len] = '\0';
309 gboolean
310 mappings(const Arg *arg) {
311 char line[255];
313 if (!arg->s) {
314 set_error("Missing argument.");
315 return FALSE;
317 strncpy(line, arg->s, 254);
318 if (process_map_line(line))
319 return TRUE;
320 else {
321 set_error("Invalid mapping.");
322 return FALSE;
326 int
327 get_modkey(char key) {
328 switch (key) {
329 case '1':
330 return GDK_MOD1_MASK;
331 case '2':
332 return GDK_MOD2_MASK;
333 case '3':
334 return GDK_MOD3_MASK;
335 case '4':
336 return GDK_MOD4_MASK;
337 case '5':
338 return GDK_MOD5_MASK;
339 default:
340 return FALSE;
344 gboolean
345 process_mapping(char *keystring, int maprecord, char *cmd) {
346 Key search_key;
348 search_key.mask = 0;
349 search_key.modkey = 0;
350 search_key.key = 0;
352 if (strlen(keystring) == 1) {
353 search_key.key = keystring[0];
356 if (strlen(keystring) == 2) {
357 search_key.modkey= keystring[0];
358 search_key.key = keystring[1];
361 /* process stuff like <S-v> for Shift-v or <C-v> for Ctrl-v (strlen == 5),
362 stuff like <S-v>a for Shift-v,a or <C-v>a for Ctrl-v,a (strlen == 6 && keystring[4] == '>')
363 stuff like <M1-v> for Mod1-v (strlen == 6 && keystring[5] == '>')
364 or stuff like <M1-v>a for Mod1-v,a (strlen = 7)
366 if (
367 ((strlen(keystring) == 5 || strlen(keystring) == 6) && keystring[0] == '<' && keystring[4] == '>') ||
368 ((strlen(keystring) == 6 || strlen(keystring) == 7) && keystring[0] == '<' && keystring[5] == '>')
370 switch (toupper(keystring[1])) {
371 case 'S':
372 search_key.mask = GDK_SHIFT_MASK;
373 if (strlen(keystring) == 5) {
374 keystring[3] = toupper(keystring[3]);
375 } else {
376 keystring[3] = tolower(keystring[3]);
377 keystring[5] = toupper(keystring[5]);
379 break;
380 case 'C':
381 search_key.mask = GDK_CONTROL_MASK;
382 break;
383 case 'M':
384 search_key.mask = get_modkey(keystring[2]);
385 break;
387 if (!search_key.mask)
388 return FALSE;
389 if (strlen(keystring) == 5) {
390 search_key.key = keystring[3];
391 } else if (strlen(keystring) == 7) {
392 search_key.modkey = keystring[4];
393 search_key.key = keystring[6];
394 } else {
395 if (search_key.mask == 'S' || search_key.mask == 'C') {
396 search_key.modkey = keystring[3];
397 search_key.key = keystring[5];
398 } else {
399 search_key.key = keystring[4];
404 /* process stuff like a<S-v> for a,Shift-v or a<C-v> for a,Ctrl-v (strlen == 6)
405 or stuff like a<M1-v> for a,Mod1-v (strlen == 7)
407 if (
408 (strlen(keystring) == 6 && keystring[1] == '<' && keystring[5] == '>') ||
409 (strlen(keystring) == 7 && keystring[1] == '<' && keystring[6] == '>')
411 switch (toupper(keystring[2])) {
412 case 'S':
413 search_key.mask = GDK_SHIFT_MASK;
414 keystring[4] = toupper(keystring[4]);
415 break;
416 case 'C':
417 search_key.mask = GDK_CONTROL_MASK;
418 break;
419 case 'M':
420 search_key.mask = get_modkey(keystring[3]);
421 break;
423 if (!search_key.mask)
424 return FALSE;
425 search_key.modkey= keystring[0];
426 if (strlen(keystring) == 6) {
427 search_key.key = keystring[4];
428 } else {
429 search_key.key = keystring[5];
432 return (changemapping(&search_key, maprecord, cmd));
435 gboolean
436 process_map_line(char *line) {
437 int listlen, i;
438 char *c, *cmd;
439 my_pair.line = line;
440 c = search_word(0);
442 if (!strlen(my_pair.what))
443 return FALSE;
444 while (isspace(*c) && *c)
445 c++;
447 if (*c == ':' || *c == '=')
448 c++;
449 my_pair.line = c;
450 c = search_word(1);
451 if (!strlen(my_pair.value))
452 return FALSE;
453 listlen = LENGTH(commands);
454 for (i = 0; i < listlen; i++) {
455 /* commands is fixed size */
456 if (commands[i].cmd == NULL)
457 break;
458 if (strlen(commands[i].cmd) == strlen(my_pair.value) && strncmp(commands[i].cmd, my_pair.value, strlen(my_pair.value)) == 0) {
459 /* map to an internal symbol */
460 return process_mapping(my_pair.what, i, NULL);
463 /* if this is reached, the mapping is not for one of the internal symbol - test for command line structure */
464 if (strlen(my_pair.value) > 1 && strncmp(my_pair.value, ":", 1) == 0) {
465 /* The string begins with a colon, like a command line, but it's not _just_ a colon,
466 * i.e. increasing the pointer by one will not go 'out of bounds'.
467 * We don't actually check that the command line after the = is valid.
468 * This is user responsibility, the worst case is the new mapping simply doing nothing.
469 * Since we will pass the command to the same function which also handles the config file lines,
470 * we have to strip the colon itself (a colon counts as a commented line there - like in vim).
471 * Last, but not least, the second argument being < 0 signifies to the function that this is a
472 * command line mapping, not a mapping to an existing internal symbol. */
473 cmd = (char *)malloc(sizeof(char) * strlen(my_pair.value));
474 strncpy(cmd, (my_pair.value + 1), strlen(my_pair.value) - 1);
475 cmd[strlen(cmd)] = '\0';
476 return process_mapping(my_pair.what, -1, cmd);
478 return FALSE;
481 gboolean
482 build_taglist(const Arg *arg, FILE *f) {
483 int k = 0, in_tag = 0;
484 int t = 0, marker = 0;
485 char foundtab[MAXTAGSIZE+1];
486 while (arg->s[k]) {
487 if (!isspace(arg->s[k]) && !in_tag) {
488 in_tag = 1;
489 marker = k;
491 if (isspace(arg->s[k]) && in_tag) {
492 /* found a tag */
493 t = 0;
494 while (marker < k && t < MAXTAGSIZE) foundtab[t++] = arg->s[marker++];
495 foundtab[t] = '\0';
496 fprintf(f, " [%s]", foundtab);
497 in_tag = 0;
499 k++;
501 if (in_tag) {
502 t = 0;
503 while (marker < strlen(arg->s) && t < MAXTAGSIZE) foundtab[t++] = arg->s[marker++];
504 foundtab[t] = '\0';
505 fprintf(f, " [%s]", foundtab );
507 return TRUE;
510 void
511 set_error(const char *error) {
512 /* it should never happen that set_error is called more than once,
513 * but to avoid any potential memory leaks, we ignore any subsequent
514 * error if the current one has not been shown */
515 if (error_msg == NULL) {
516 error_msg = g_strdup_printf("%s", error);
520 void
521 give_feedback(const char *feedback)
523 Arg a = { .i = Info };
525 a.s = g_strdup_printf("%s", feedback);
526 echo(&a);
527 g_free(a.s);
530 Listelement *
531 complete_list(const char *searchfor, const int mode, Listelement *elementlist)
533 FILE *f;
534 const char *filename;
535 Listelement *candidatelist = NULL, *candidatepointer = NULL;
536 char s[255] = "", readelement[MAXTAGSIZE + 1] = "";
537 int i, t, n = 0;
539 if (mode == 2) {
540 /* open in history file */
541 filename = g_strdup_printf(HISTORY_STORAGE_FILENAME);
542 } else {
543 /* open in bookmark file (for tags and bookmarks) */
544 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
546 f = fopen(filename, "r");
547 if (f == NULL) {
548 g_free((gpointer *)filename);
549 return (elementlist);
552 while (fgets(s, 254, f)) {
553 if (mode == 1) {
554 /* just tags (could be more than one per line) */
555 i = 0;
556 while (s[i] && i < 254) {
557 while (s[i] != '[' && s[i])
558 i++;
559 if (s[i] != '[')
560 continue;
561 i++;
562 t = 0;
563 while (s[i] != ']' && s[i] && t < MAXTAGSIZE)
564 readelement[t++] = s[i++];
565 readelement[t] = '\0';
566 candidatelist = add_list(readelement, candidatelist);
567 i++;
569 } else {
570 /* complete string (bookmarks & history) */
571 candidatelist = add_list(s, candidatelist);
573 candidatepointer = candidatelist;
574 while (candidatepointer != NULL) {
575 strncpy(s, candidatepointer->element, sizeof(s));
576 if (!complete_case_sensitive) {
577 g_strdown(s);
579 if (!strlen(searchfor) || strstr(s, searchfor) != NULL) {
580 /* only use string up to the first space */
581 memset(readelement, 0, MAXTAGSIZE + 1);
582 if (strchr(candidatepointer->element, ' ') != NULL) {
583 i = strcspn(candidatepointer->element, " ");
584 if (i > MAXTAGSIZE)
585 i = MAXTAGSIZE;
586 strncpy(readelement, candidatepointer->element, i);
587 } else {
588 strncpy(readelement, candidatepointer->element, MAXTAGSIZE);
590 /* in the case of URLs without title, remove the line break */
591 if (readelement[strlen(readelement) - 1] == '\n') {
592 readelement[strlen(readelement) - 1] = '\0';
594 elementlist = add_list(readelement, elementlist);
595 n = count_list(elementlist);
597 if (n >= MAX_LIST_SIZE)
598 break;
599 candidatepointer = candidatepointer->next;
601 free_list(candidatelist);
602 candidatelist = NULL;
603 if (n >= MAX_LIST_SIZE)
604 break;
606 g_free((gpointer)filename);
607 return (elementlist);
610 Listelement *
611 add_list(const char *element, Listelement *elementlist)
613 int n, i = 0;
614 Listelement *newelement, *elementpointer, *lastelement;
616 if (elementlist == NULL) { /* first element */
617 newelement = malloc(sizeof(Listelement));
618 if (newelement == NULL)
619 return (elementlist);
620 strncpy(newelement->element, element, 254);
621 newelement->next = NULL;
622 return newelement;
624 elementpointer = elementlist;
625 n = strlen(element);
627 /* check if element is already in list */
628 while (elementpointer != NULL) {
629 if (strlen(elementpointer->element) == n &&
630 strncmp(elementpointer->element, element, n) == 0)
631 return (elementlist);
632 lastelement = elementpointer;
633 elementpointer = elementpointer->next;
634 i++;
636 /* add to list */
637 newelement = malloc(sizeof(Listelement));
638 if (newelement == NULL)
639 return (elementlist);
640 lastelement->next = newelement;
641 strncpy(newelement->element, element, 254);
642 newelement->next = NULL;
643 return elementlist;
646 void
647 free_list(Listelement *elementlist)
649 Listelement *elementpointer;
651 while (elementlist != NULL) {
652 elementpointer = elementlist->next;
653 free(elementlist);
654 elementlist = elementpointer;
659 count_list(Listelement *elementlist)
661 Listelement *elementpointer = elementlist;
662 int n = 0;
664 while (elementpointer != NULL) {
665 n++;
666 elementpointer = elementpointer->next;
669 return n;
672 /* split the string at the first occurence of whitespace and return the
673 * position of the second half.
674 * Unlike strtok, the substrings can be empty and the second string is
675 * stripped of trailing and leading whitespace.
676 * Return -1 if `string' contains no whitespace */
677 static int split_string_at_whitespace(char *string)
679 int index = strcspn(string, "\n\t ");
680 if (string[index] != '\0') {
681 string[index++] = 0;
682 g_strstrip(string+index);
683 return index;
685 return -1;
688 /* return TRUE, if the string contains exactly one unescaped %s and no other
689 * printf directives */
690 static gboolean sanity_check_search_url(const char *string)
692 int was_percent_char = 0, percent_s_count = 0;
694 for (; *string; string++) {
695 switch (*string) {
696 case '%':
697 was_percent_char = !was_percent_char;
698 break;
699 case 's':
700 if (was_percent_char)
701 percent_s_count++;
702 was_percent_char = 0;
703 break;
704 default:
705 if (was_percent_char)
706 return FALSE;
707 was_percent_char = 0;
708 break;
712 return !was_percent_char && percent_s_count == 1;
715 enum ConfigFileError
716 read_searchengines(const char *filename)
718 FILE *file;
719 char buffer[BUFFERSIZE], c;
720 int linum = 0, index;
721 gboolean found_malformed_lines = FALSE;
722 Searchengine *new;
724 if (access(filename, F_OK) != 0)
725 return FILE_NOT_FOUND;
727 file = fopen(filename, "r");
728 if (file == NULL)
729 return READING_FAILED;
731 while (fgets(buffer, BUFFERSIZE, file)) {
732 linum++;
734 /* skip empty lines */
735 if (!strcmp(buffer, "\n")) continue;
737 /* skip too long lines */
738 if (buffer[strlen(buffer)-1] != '\n') {
739 c = getc(file);
740 if (c != EOF) { /* this is not the last line */
741 while ((c=getc(file)) != EOF && c != '\n');
742 fprintf(stderr, "searchengines: syntax error on line %d\n", linum);
743 found_malformed_lines = TRUE;
744 continue;
748 /* split line at whitespace */
749 index = split_string_at_whitespace(buffer);
751 if (index < 0 || buffer[0] == '\0' || buffer[index] == '\0'
752 || !sanity_check_search_url(buffer+index)) {
753 fprintf(stderr, "searchengines: syntax error on line %d\n", linum);
754 found_malformed_lines = TRUE;
755 continue;
758 new = malloc(sizeof(Searchengine));
759 if (new == NULL) {
760 fprintf(stderr, "Memory exhausted while loading search engines.\n");
761 exit(EXIT_FAILURE);
764 new->handle = g_strdup(buffer);
765 new->uri = g_strdup(buffer+index);
767 dynamic_searchengines = g_list_prepend(dynamic_searchengines, new);
770 if (ferror(file)) {
771 fclose(file);
772 return READING_FAILED;
775 fclose(file);
777 return found_malformed_lines ? SYNTAX_ERROR : SUCCESS;
780 void make_searchengines_list(Searchengine *searchengines, int length)
782 int i;
783 for (i = 0; i < length; i++, searchengines++) {
784 dynamic_searchengines = g_list_prepend(dynamic_searchengines, searchengines);
788 /* find a searchengine with a given handle and return its URI or NULL if
789 * nothing is found.
790 * The returned string is internal and must not be freed or modified. */
791 char *find_uri_for_searchengine(const char *handle)
793 GList *l;
795 if (dynamic_searchengines != NULL) {
796 for (l = dynamic_searchengines; l; l = g_list_next(l)) {
797 Searchengine *s = (Searchengine*)l->data;
798 if (!strcmp(s->handle, handle)) {
799 return s->uri;
804 return NULL;