respect DESTDIR in 'make uninstall'
[vimprobable2.git] / utilities.c
blob7adac1fa3f064b77af244d3212e794d7f578aad6
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 gboolean read_rcfile(const char *config)
27 int t;
28 char s[255];
29 FILE *fpin;
30 gboolean returnval = TRUE;
32 if ((fpin = fopen(config, "r")) == NULL)
33 return FALSE;
34 while (fgets(s, 254, fpin)) {
36 * ignore lines that begin with #, / and such
38 if (!isalpha(s[0]))
39 continue;
40 t = strlen(s);
41 s[t - 1] = '\0';
42 if (!process_line(s))
43 returnval = FALSE;
45 fclose(fpin);
46 return returnval;
49 void save_command_history(char *line)
51 char *c;
53 c = line;
54 while (isspace(*c) && *c)
55 c++;
56 if (!strlen(c))
57 return;
58 strncpy(commandhistory[lastcommand], ":", 1);
59 strncat(commandhistory[lastcommand], c, 254);
60 lastcommand++;
61 if (maxcommands < COMMANDHISTSIZE - 1)
62 maxcommands++;
63 if (lastcommand == COMMANDHISTSIZE)
64 lastcommand = 0;
65 commandpointer = lastcommand;
68 gboolean
69 process_save_qmark(const char *bm, WebKitWebView *webview)
71 FILE *fp;
72 const char *filename;
73 const char *uri = webkit_web_view_get_uri(webview);
74 char qmarks[10][101];
75 char buf[100];
76 int i, mark, l=0;
77 Arg a;
78 mark = -1;
79 mark = atoi(bm);
80 if ( mark < 1 || mark > 9 )
82 a.i = Error;
83 a.s = g_strdup_printf("Invalid quickmark, only 1-9");
84 echo(&a);
85 g_free(a.s);
86 return TRUE;
88 if ( uri == NULL ) return FALSE;
89 for( i=0; i < 9; ++i ) strcpy( qmarks[i], "");
91 filename = g_strdup_printf(QUICKMARK_FILE);
93 /* get current quickmarks */
95 fp = fopen(filename, "r");
96 if (fp != NULL){
97 for( i=0; i < 10; ++i ) {
98 if (feof(fp)) {
99 break;
101 fgets(buf, 100, fp);
102 l = 0;
103 while (buf[l] && l < 100 && buf[l] != '\n') {
104 qmarks[i][l]=buf[l];
105 l++;
107 qmarks[i][l]='\0';
109 fclose(fp);
112 /* save quickmarks */
113 strcpy( qmarks[mark-1], uri );
114 fp = fopen(filename, "w");
115 g_free((gpointer *)filename);
116 if (fp == NULL) return FALSE;
117 for( i=0; i < 10; ++i )
118 fprintf(fp, "%s\n", qmarks[i]);
119 fclose(fp);
120 a.i = Error;
121 a.s = g_strdup_printf("Saved as quickmark %d: %s", mark, uri);
122 echo(&a);
123 g_free(a.s);
125 return TRUE;
128 void
129 make_keyslist(void)
131 int i;
132 KeyList *ptr, *current;
134 ptr = NULL;
135 current = NULL;
136 i = 0;
137 while ( keys[i].key != 0 )
139 current = malloc(sizeof(KeyList));
140 if (current == NULL) {
141 printf("Not enough memory\n");
142 exit(-1);
144 current->Element = keys[i];
145 current->next = NULL;
146 if (keylistroot == NULL) keylistroot = current;
147 if (ptr != NULL) ptr->next = current;
148 ptr = current;
149 i++;
153 gboolean
154 parse_colour(char *color) {
155 char goodcolor[8];
156 int colorlen;
158 colorlen = (int)strlen(color);
160 goodcolor[0] = '#';
161 goodcolor[7] = '\0';
163 /* help the user a bit by making string like
164 #a10 and strings like ffffff full 6digit
165 strings with # in front :)
168 if (color[0] == '#') {
169 switch (colorlen) {
170 case 7:
171 strncpy(goodcolor, color, 7);
172 break;
173 case 4:
174 goodcolor[1] = color[1];
175 goodcolor[2] = color[1];
176 goodcolor[3] = color[2];
177 goodcolor[4] = color[2];
178 goodcolor[5] = color[3];
179 goodcolor[6] = color[3];
180 break;
181 case 2:
182 goodcolor[1] = color[1];
183 goodcolor[2] = color[1];
184 goodcolor[3] = color[1];
185 goodcolor[4] = color[1];
186 goodcolor[5] = color[1];
187 goodcolor[6] = color[1];
188 break;
190 } else {
191 switch (colorlen) {
192 case 6:
193 strncpy(&goodcolor[1], color, 6);
194 break;
195 case 3:
196 goodcolor[1] = color[0];
197 goodcolor[2] = color[0];
198 goodcolor[3] = color[1];
199 goodcolor[4] = color[1];
200 goodcolor[5] = color[2];
201 goodcolor[6] = color[2];
202 break;
203 case 1:
204 goodcolor[1] = color[0];
205 goodcolor[2] = color[0];
206 goodcolor[3] = color[0];
207 goodcolor[4] = color[0];
208 goodcolor[5] = color[0];
209 goodcolor[6] = color[0];
210 break;
214 if (strlen (goodcolor) != 7) {
215 return FALSE;
216 } else {
217 strncpy(color, goodcolor, 8);
218 return TRUE;
222 gboolean
223 process_line_arg(const Arg *arg) {
224 return process_line(arg->s);
227 gboolean
228 changemapping(Key *search_key, int maprecord, char *cmd) {
229 KeyList *current, *newkey;
230 Arg a = { .s = cmd };
232 /* sanity check */
233 if (maprecord < 0 && cmd == NULL) {
234 /* possible states:
235 * - maprecord >= 0 && cmd == NULL: mapping to internal symbol
236 * - maprecord < 0 && cmd != NULL: mapping to command line
237 * - maprecord >= 0 && cmd != NULL: cmd will be ignored, treated as mapping to internal symbol
238 * - anything else (gets in here): an error, hence we return FALSE */
239 return FALSE;
242 current = keylistroot;
244 if (current)
245 while (current->next != NULL) {
246 if (
247 current->Element.mask == search_key->mask &&
248 current->Element.modkey == search_key->modkey &&
249 current->Element.key == search_key->key
251 if (maprecord >= 0) {
252 /* mapping to an internal signal */
253 current->Element.func = commands[maprecord].func;
254 current->Element.arg = commands[maprecord].arg;
255 } else {
256 /* mapping to a command line */
257 current->Element.func = process_line_arg;
258 current->Element.arg = a;
260 return TRUE;
262 current = current->next;
264 newkey = malloc(sizeof(KeyList));
265 if (newkey == NULL) {
266 printf("Not enough memory\n");
267 exit (-1);
269 newkey->Element.mask = search_key->mask;
270 newkey->Element.modkey = search_key->modkey;
271 newkey->Element.key = search_key->key;
272 if (maprecord >= 0) {
273 /* mapping to an internal signal */
274 newkey->Element.func = commands[maprecord].func;
275 newkey->Element.arg = commands[maprecord].arg;
276 } else {
277 /* mapping to a command line */
278 newkey->Element.func = process_line_arg;
279 newkey->Element.arg = a;
281 newkey->next = NULL;
283 if (keylistroot == NULL) keylistroot = newkey;
285 if (current != NULL) current->next = newkey;
287 return TRUE;
290 gboolean
291 mappings(const Arg *arg) {
292 char line[255];
294 if (!arg->s) {
295 set_error("Missing argument.");
296 return FALSE;
298 strncpy(line, arg->s, 254);
299 if (process_map_line(line))
300 return TRUE;
301 else {
302 set_error("Invalid mapping.");
303 return FALSE;
307 int
308 get_modkey(char key) {
309 switch (key) {
310 case '1':
311 return GDK_MOD1_MASK;
312 case '2':
313 return GDK_MOD2_MASK;
314 case '3':
315 return GDK_MOD3_MASK;
316 case '4':
317 return GDK_MOD4_MASK;
318 case '5':
319 return GDK_MOD5_MASK;
320 default:
321 return FALSE;
325 gboolean
326 process_mapping(char *keystring, int maprecord, char *cmd) {
327 Key search_key;
329 search_key.mask = 0;
330 search_key.modkey = 0;
331 search_key.key = 0;
333 if (strlen(keystring) == 1) {
334 search_key.key = keystring[0];
337 if (strlen(keystring) == 2) {
338 search_key.modkey= keystring[0];
339 search_key.key = keystring[1];
342 /* process stuff like <S-v> for Shift-v or <C-v> for Ctrl-v (strlen == 5),
343 stuff like <S-v>a for Shift-v,a or <C-v>a for Ctrl-v,a (strlen == 6 && keystring[4] == '>')
344 stuff like <M1-v> for Mod1-v (strlen == 6 && keystring[5] == '>')
345 or stuff like <M1-v>a for Mod1-v,a (strlen = 7)
347 if (
348 ((strlen(keystring) == 5 || strlen(keystring) == 6) && keystring[0] == '<' && keystring[4] == '>') ||
349 ((strlen(keystring) == 6 || strlen(keystring) == 7) && keystring[0] == '<' && keystring[5] == '>')
351 switch (toupper(keystring[1])) {
352 case 'S':
353 search_key.mask = GDK_SHIFT_MASK;
354 if (strlen(keystring) == 5) {
355 keystring[3] = toupper(keystring[3]);
356 } else {
357 keystring[3] = tolower(keystring[3]);
358 keystring[5] = toupper(keystring[5]);
360 break;
361 case 'C':
362 search_key.mask = GDK_CONTROL_MASK;
363 break;
364 case 'M':
365 search_key.mask = get_modkey(keystring[2]);
366 break;
368 if (!search_key.mask)
369 return FALSE;
370 if (strlen(keystring) == 5) {
371 search_key.key = keystring[3];
372 } else if (strlen(keystring) == 7) {
373 search_key.modkey = keystring[4];
374 search_key.key = keystring[6];
375 } else {
376 if (search_key.mask == 'S' || search_key.mask == 'C') {
377 search_key.modkey = keystring[3];
378 search_key.key = keystring[5];
379 } else {
380 search_key.key = keystring[4];
385 /* process stuff like a<S-v> for a,Shift-v or a<C-v> for a,Ctrl-v (strlen == 6)
386 or stuff like a<M1-v> for a,Mod1-v (strlen == 7)
388 if (
389 (strlen(keystring) == 6 && keystring[1] == '<' && keystring[5] == '>') ||
390 (strlen(keystring) == 7 && keystring[1] == '<' && keystring[6] == '>')
392 switch (toupper(keystring[2])) {
393 case 'S':
394 search_key.mask = GDK_SHIFT_MASK;
395 keystring[4] = toupper(keystring[4]);
396 break;
397 case 'C':
398 search_key.mask = GDK_CONTROL_MASK;
399 break;
400 case 'M':
401 search_key.mask = get_modkey(keystring[3]);
402 break;
404 if (!search_key.mask)
405 return FALSE;
406 search_key.modkey= keystring[0];
407 if (strlen(keystring) == 6) {
408 search_key.key = keystring[4];
409 } else {
410 search_key.key = keystring[5];
413 return (changemapping(&search_key, maprecord, cmd));
416 gboolean
417 process_map_line(char *line) {
418 int listlen, i;
419 char *c, *cmd;
420 my_pair.line = line;
421 c = search_word(0);
423 if (!strlen(my_pair.what))
424 return FALSE;
425 while (isspace(*c) && *c)
426 c++;
428 if (*c == ':' || *c == '=')
429 c++;
430 my_pair.line = c;
431 c = search_word(1);
432 if (!strlen(my_pair.value))
433 return FALSE;
434 listlen = LENGTH(commands);
435 for (i = 0; i < listlen; i++) {
436 /* commands is fixed size */
437 if (commands[i].cmd == NULL)
438 break;
439 if (strlen(commands[i].cmd) == strlen(my_pair.value) && strncmp(commands[i].cmd, my_pair.value, strlen(my_pair.value)) == 0) {
440 /* map to an internal symbol */
441 return process_mapping(my_pair.what, i, NULL);
444 /* if this is reached, the mapping is not for one of the internal symbol - test for command line structure */
445 if (strlen(my_pair.value) > 1 && strncmp(my_pair.value, ":", 1) == 0) {
446 /* The string begins with a colon, like a command line, but it's not _just_ a colon,
447 * i.e. increasing the pointer by one will not go 'out of bounds'.
448 * We don't actually check that the command line after the = is valid.
449 * This is user responsibility, the worst case is the new mapping simply doing nothing.
450 * Since we will pass the command to the same function which also handles the config file lines,
451 * we have to strip the colon itself (a colon counts as a commented line there - like in vim).
452 * Last, but not least, the second argument being < 0 signifies to the function that this is a
453 * command line mapping, not a mapping to an existing internal symbol. */
454 cmd = (char *)malloc(sizeof(char) * strlen(my_pair.value));
455 strncpy(cmd, (my_pair.value + 1), strlen(my_pair.value) - 1);
456 cmd[strlen(cmd)] = '\0';
457 return process_mapping(my_pair.what, -1, cmd);
459 return FALSE;
462 gboolean
463 build_taglist(const Arg *arg, FILE *f) {
464 int k = 0, in_tag = 0;
465 int t = 0, marker = 0;
466 char foundtab[MAXTAGSIZE+1];
467 while (arg->s[k]) {
468 if (!isspace(arg->s[k]) && !in_tag) {
469 in_tag = 1;
470 marker = k;
472 if (isspace(arg->s[k]) && in_tag) {
473 /* found a tag */
474 t = 0;
475 while (marker < k && t < MAXTAGSIZE) foundtab[t++] = arg->s[marker++];
476 foundtab[t] = '\0';
477 fprintf(f, " [%s]", foundtab);
478 in_tag = 0;
480 k++;
482 if (in_tag) {
483 t = 0;
484 while (marker < strlen(arg->s) && t < MAXTAGSIZE) foundtab[t++] = arg->s[marker++];
485 foundtab[t] = '\0';
486 fprintf(f, " [%s]", foundtab );
488 return TRUE;
491 void
492 set_error(const char *error) {
493 /* it should never happen that set_error is called more than once,
494 * but to avoid any potential memory leaks, we ignore any subsequent
495 * error if the current one has not been shown */
496 if (error_msg == NULL) {
497 error_msg = g_strdup_printf("%s", error);
501 void
502 give_feedback(const char *feedback)
504 Arg a = { .i = Info };
506 a.s = g_strdup_printf("%s", feedback);
507 echo(&a);
508 g_free(a.s);
511 Listelement *
512 complete_list(const char *searchfor, const int mode, Listelement *elementlist)
514 FILE *f;
515 const char *filename;
516 Listelement *candidatelist = NULL, *candidatepointer = NULL;
517 char s[255] = "", readelement[MAXTAGSIZE + 1] = "";
518 int i, t, n = 0;
520 if (mode == 2) {
521 /* open in history file */
522 filename = g_strdup_printf(HISTORY_STORAGE_FILENAME);
523 } else {
524 /* open in bookmark file (for tags and bookmarks) */
525 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
527 f = fopen(filename, "r");
528 if (f == NULL) {
529 g_free((gpointer *)filename);
530 return (elementlist);
533 while (fgets(s, 254, f)) {
534 if (mode == 1) {
535 /* just tags (could be more than one per line) */
536 i = 0;
537 while (s[i] && i < 254) {
538 while (s[i] != '[' && s[i])
539 i++;
540 if (s[i] != '[')
541 continue;
542 i++;
543 t = 0;
544 while (s[i] != ']' && s[i] && t < MAXTAGSIZE)
545 readelement[t++] = s[i++];
546 readelement[t] = '\0';
547 candidatelist = add_list(readelement, candidatelist);
548 i++;
550 } else {
551 /* complete string (bookmarks & history) */
552 candidatelist = add_list(s, candidatelist);
554 candidatepointer = candidatelist;
555 while (candidatepointer != NULL) {
556 if (!complete_case_sensitive) {
557 g_strdown(candidatepointer->element);
559 if (!strlen(searchfor) || strstr(candidatepointer->element, searchfor) != NULL) {
560 /* only use string up to the first space */
561 memset(readelement, 0, MAXTAGSIZE + 1);
562 if (strchr(candidatepointer->element, ' ') != NULL) {
563 i = strcspn(candidatepointer->element, " ");
564 if (i > MAXTAGSIZE)
565 i = MAXTAGSIZE;
566 strncpy(readelement, candidatepointer->element, i);
567 } else {
568 strncpy(readelement, candidatepointer->element, MAXTAGSIZE);
570 /* in the case of URLs without title, remove the line break */
571 if (readelement[strlen(readelement) - 1] == '\n') {
572 readelement[strlen(readelement) - 1] = '\0';
574 elementlist = add_list(readelement, elementlist);
575 n = count_list(elementlist);
577 if (n >= MAX_LIST_SIZE)
578 break;
579 candidatepointer = candidatepointer->next;
581 free_list(candidatelist);
582 candidatelist = NULL;
583 if (n >= MAX_LIST_SIZE)
584 break;
586 g_free((gpointer)filename);
587 return (elementlist);
590 Listelement *
591 add_list(const char *element, Listelement *elementlist)
593 int n, i = 0;
594 Listelement *newelement, *elementpointer, *lastelement;
596 if (elementlist == NULL) { /* first element */
597 newelement = malloc(sizeof(Listelement));
598 if (newelement == NULL)
599 return (elementlist);
600 strncpy(newelement->element, element, 254);
601 newelement->next = NULL;
602 return newelement;
604 elementpointer = elementlist;
605 n = strlen(element);
607 /* check if element is already in list */
608 while (elementpointer != NULL) {
609 if (strlen(elementpointer->element) == n &&
610 strncmp(elementpointer->element, element, n) == 0)
611 return (elementlist);
612 lastelement = elementpointer;
613 elementpointer = elementpointer->next;
614 i++;
616 /* add to list */
617 newelement = malloc(sizeof(Listelement));
618 if (newelement == NULL)
619 return (elementlist);
620 lastelement->next = newelement;
621 strncpy(newelement->element, element, 254);
622 newelement->next = NULL;
623 return elementlist;
626 void
627 free_list(Listelement *elementlist)
629 Listelement *elementpointer;
631 while (elementlist != NULL) {
632 elementpointer = elementlist->next;
633 free(elementlist);
634 elementlist = elementpointer;
639 count_list(Listelement *elementlist)
641 Listelement *elementpointer = elementlist;
642 int n = 0;
644 while (elementpointer != NULL) {
645 n++;
646 elementpointer = elementpointer->next;
649 return n;
652 /* split the string at the first occurence of whitespace and return the
653 * position of the second half.
654 * Unlike strtok, the substrings can be empty and the second string is
655 * stripped of trailing and leading whitespace.
656 * Return -1 if `string' contains no whitespace */
657 static int split_string_at_whitespace(char *string)
659 int index = strcspn(string, "\n\t ");
660 if (string[index] != '\0') {
661 string[index++] = 0;
662 g_strstrip(string+index);
663 return index;
665 return -1;
668 /* return TRUE, if the string contains exactly one unescaped %s and no other
669 * printf directives */
670 static gboolean sanity_check_search_url(const char *string)
672 int was_percent_char = 0, percent_s_count = 0;
674 for (; *string; string++) {
675 switch (*string) {
676 case '%':
677 was_percent_char = !was_percent_char;
678 break;
679 case 's':
680 if (was_percent_char)
681 percent_s_count++;
682 was_percent_char = 0;
683 break;
684 default:
685 if (was_percent_char)
686 return FALSE;
687 was_percent_char = 0;
688 break;
692 return !was_percent_char && percent_s_count == 1;
695 enum ConfigFileError
696 read_searchengines(const char *filename)
698 FILE *file;
699 char buffer[BUFFERSIZE], c;
700 int linum = 0, index;
701 gboolean found_malformed_lines = FALSE;
702 Searchengine *new;
704 if (access(filename, F_OK) != 0)
705 return FILE_NOT_FOUND;
707 file = fopen(filename, "r");
708 if (file == NULL)
709 return READING_FAILED;
711 while (fgets(buffer, BUFFERSIZE, file)) {
712 linum++;
714 /* skip empty lines */
715 if (!strcmp(buffer, "\n")) continue;
717 /* skip too long lines */
718 if (buffer[strlen(buffer)-1] != '\n') {
719 c = getc(file);
720 if (c != EOF) { /* this is not the last line */
721 while ((c=getc(file)) != EOF && c != '\n');
722 fprintf(stderr, "searchengines: syntax error on line %d\n", linum);
723 found_malformed_lines = TRUE;
724 continue;
728 /* split line at whitespace */
729 index = split_string_at_whitespace(buffer);
731 if (index < 0 || buffer[0] == '\0' || buffer[index] == '\0'
732 || !sanity_check_search_url(buffer+index)) {
733 fprintf(stderr, "searchengines: syntax error on line %d\n", linum);
734 found_malformed_lines = TRUE;
735 continue;
738 new = malloc(sizeof(Searchengine));
739 if (new == NULL) {
740 fprintf(stderr, "Memory exhausted while loading search engines.\n");
741 exit(EXIT_FAILURE);
744 new->handle = g_strdup(buffer);
745 new->uri = g_strdup(buffer+index);
747 dynamic_searchengines = g_list_prepend(dynamic_searchengines, new);
750 if (ferror(file)) {
751 fclose(file);
752 return READING_FAILED;
755 fclose(file);
757 return found_malformed_lines ? SYNTAX_ERROR : SUCCESS;
760 void make_searchengines_list(Searchengine *searchengines, int length)
762 int i;
763 for (i = 0; i < length; i++, searchengines++) {
764 dynamic_searchengines = g_list_prepend(dynamic_searchengines, searchengines);
768 /* find a searchengine with a given handle and return its URI or NULL if
769 * nothing is found.
770 * The returned string is internal and must not be freed or modified. */
771 char *find_uri_for_searchengine(const char *handle)
773 GList *l;
775 if (dynamic_searchengines != NULL) {
776 for (l = dynamic_searchengines; l; l = g_list_next(l)) {
777 Searchengine *s = (Searchengine*)l->data;
778 if (!strcmp(s->handle, handle)) {
779 return s->uri;
784 return NULL;