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
11 #include "vimprobable.h"
13 #include "utilities.h"
15 extern GList
*commandhistory
;
16 extern int commandpointer
;
17 extern Command commands
[COMMANDSIZE
];
18 extern KeyList
*keylistroot
;
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 void save_command_history(char *line
)
31 while (isspace(*c
) && *c
)
36 if (COMMANDHISTSIZE
<= g_list_length(commandhistory
)) {
37 /* if list is too long - remove items from beginning */
38 commandhistory
= g_list_delete_link(commandhistory
, g_list_first(commandhistory
));
40 commandhistory
= g_list_append(commandhistory
, g_strdup(c
));
44 process_save_qmark(const char *bm
, WebKitWebView
*webview
)
48 const char *uri
= webkit_web_view_get_uri(webview
);
55 if ( mark
< 1 || mark
> 9 )
58 a
.s
= g_strdup_printf("Invalid quickmark, only 1-9");
63 if ( uri
== NULL
) return FALSE
;
64 for( i
=0; i
< 9; ++i
) strcpy( qmarks
[i
], "");
66 filename
= g_strdup_printf(QUICKMARK_FILE
);
68 /* get current quickmarks */
70 fp
= fopen(filename
, "r");
72 for( i
=0; i
< 10; ++i
) {
78 while (buf
[l
] && l
< 100 && buf
[l
] != '\n') {
88 strcpy( qmarks
[mark
-1], uri
);
89 fp
= fopen(filename
, "w");
90 g_free((gpointer
*)filename
);
91 if (fp
== NULL
) return FALSE
;
92 for( i
=0; i
< 10; ++i
)
93 fprintf(fp
, "%s\n", qmarks
[i
]);
96 a
.s
= g_strdup_printf("Saved as quickmark %d: %s", mark
, uri
);
107 KeyList
*ptr
, *current
;
112 while ( keys
[i
].key
!= 0 )
114 current
= malloc(sizeof(KeyList
));
115 if (current
== NULL
) {
116 printf("Not enough memory\n");
119 current
->Element
= keys
[i
];
120 current
->next
= NULL
;
121 if (keylistroot
== NULL
) keylistroot
= current
;
122 if (ptr
!= NULL
) ptr
->next
= current
;
129 parse_colour(char *color
) {
133 colorlen
= (int)strlen(color
);
138 /* help the user a bit by making string like
139 #a10 and strings like ffffff full 6digit
140 strings with # in front :)
143 if (color
[0] == '#') {
146 strncpy(goodcolor
, color
, 7);
149 goodcolor
[1] = color
[1];
150 goodcolor
[2] = color
[1];
151 goodcolor
[3] = color
[2];
152 goodcolor
[4] = color
[2];
153 goodcolor
[5] = color
[3];
154 goodcolor
[6] = color
[3];
157 goodcolor
[1] = color
[1];
158 goodcolor
[2] = color
[1];
159 goodcolor
[3] = color
[1];
160 goodcolor
[4] = color
[1];
161 goodcolor
[5] = color
[1];
162 goodcolor
[6] = color
[1];
168 strncpy(&goodcolor
[1], color
, 6);
171 goodcolor
[1] = color
[0];
172 goodcolor
[2] = color
[0];
173 goodcolor
[3] = color
[1];
174 goodcolor
[4] = color
[1];
175 goodcolor
[5] = color
[2];
176 goodcolor
[6] = color
[2];
179 goodcolor
[1] = color
[0];
180 goodcolor
[2] = color
[0];
181 goodcolor
[3] = color
[0];
182 goodcolor
[4] = color
[0];
183 goodcolor
[5] = color
[0];
184 goodcolor
[6] = color
[0];
189 if (strlen (goodcolor
) != 7) {
192 strncpy(color
, goodcolor
, 8);
198 process_line_arg(const Arg
*arg
) {
199 return process_line(arg
->s
);
203 changemapping(Key
*search_key
, int maprecord
, char *cmd
) {
204 KeyList
*current
, *newkey
;
205 Arg a
= { .s
= cmd
};
208 if (maprecord
< 0 && cmd
== NULL
) {
210 * - maprecord >= 0 && cmd == NULL: mapping to internal symbol
211 * - maprecord < 0 && cmd != NULL: mapping to command line
212 * - maprecord >= 0 && cmd != NULL: cmd will be ignored, treated as mapping to internal symbol
213 * - anything else (gets in here): an error, hence we return FALSE */
217 current
= keylistroot
;
220 while (current
->next
!= NULL
) {
222 current
->Element
.mask
== search_key
->mask
&&
223 current
->Element
.modkey
== search_key
->modkey
&&
224 current
->Element
.key
== search_key
->key
226 if (maprecord
>= 0) {
227 /* mapping to an internal signal */
228 current
->Element
.func
= commands
[maprecord
].func
;
229 current
->Element
.arg
= commands
[maprecord
].arg
;
231 /* mapping to a command line */
232 current
->Element
.func
= process_line_arg
;
233 current
->Element
.arg
= a
;
237 current
= current
->next
;
239 newkey
= malloc(sizeof(KeyList
));
240 if (newkey
== NULL
) {
241 printf("Not enough memory\n");
244 newkey
->Element
.mask
= search_key
->mask
;
245 newkey
->Element
.modkey
= search_key
->modkey
;
246 newkey
->Element
.key
= search_key
->key
;
247 if (maprecord
>= 0) {
248 /* mapping to an internal signal */
249 newkey
->Element
.func
= commands
[maprecord
].func
;
250 newkey
->Element
.arg
= commands
[maprecord
].arg
;
252 /* mapping to a command line */
253 newkey
->Element
.func
= process_line_arg
;
254 newkey
->Element
.arg
= a
;
256 add_modkeys(newkey
->Element
.modkey
);
260 if (keylistroot
== NULL
) keylistroot
= newkey
;
262 if (current
!= NULL
) current
->next
= newkey
;
267 void add_modkeys(char key
)
270 extern char *modkeys
;
271 len
= strlen( modkeys
);
274 if ( modkeys
[k
] == key
) return;
277 modkeys
= realloc(modkeys
, len
+ 1);
278 modkeys
[len
++] = key
;
283 mappings(const Arg
*arg
) {
287 set_error("Missing argument.");
290 strncpy(line
, arg
->s
, 254);
291 if (process_map_line(line
))
294 set_error("Invalid mapping.");
300 get_modkey(char key
) {
303 return GDK_MOD1_MASK
;
305 return GDK_MOD2_MASK
;
307 return GDK_MOD3_MASK
;
309 return GDK_MOD4_MASK
;
311 return GDK_MOD5_MASK
;
318 process_mapping(char *keystring
, int maprecord
, char *cmd
) {
322 search_key
.modkey
= 0;
325 if (strlen(keystring
) == 1) {
326 search_key
.key
= keystring
[0];
329 if (strlen(keystring
) == 2) {
330 search_key
.modkey
= keystring
[0];
331 search_key
.key
= keystring
[1];
334 /* process stuff like <S-v> for Shift-v or <C-v> for Ctrl-v (strlen == 5),
335 stuff like <S-v>a for Shift-v,a or <C-v>a for Ctrl-v,a (strlen == 6 && keystring[4] == '>')
336 stuff like <M1-v> for Mod1-v (strlen == 6 && keystring[5] == '>')
337 or stuff like <M1-v>a for Mod1-v,a (strlen = 7)
340 ((strlen(keystring
) == 5 || strlen(keystring
) == 6) && keystring
[0] == '<' && keystring
[4] == '>') ||
341 ((strlen(keystring
) == 6 || strlen(keystring
) == 7) && keystring
[0] == '<' && keystring
[5] == '>')
343 switch (toupper(keystring
[1])) {
345 search_key
.mask
= GDK_SHIFT_MASK
;
346 if (strlen(keystring
) == 5) {
347 keystring
[3] = toupper(keystring
[3]);
349 keystring
[3] = tolower(keystring
[3]);
350 keystring
[5] = toupper(keystring
[5]);
354 search_key
.mask
= GDK_CONTROL_MASK
;
357 search_key
.mask
= get_modkey(keystring
[2]);
360 if (!search_key
.mask
)
362 if (strlen(keystring
) == 5) {
363 search_key
.key
= keystring
[3];
364 } else if (strlen(keystring
) == 7) {
365 search_key
.modkey
= keystring
[4];
366 search_key
.key
= keystring
[6];
368 if (search_key
.mask
== 'S' || search_key
.mask
== 'C') {
369 search_key
.modkey
= keystring
[3];
370 search_key
.key
= keystring
[5];
372 search_key
.key
= keystring
[4];
377 /* process stuff like a<S-v> for a,Shift-v or a<C-v> for a,Ctrl-v (strlen == 6)
378 or stuff like a<M1-v> for a,Mod1-v (strlen == 7)
381 (strlen(keystring
) == 6 && keystring
[1] == '<' && keystring
[5] == '>') ||
382 (strlen(keystring
) == 7 && keystring
[1] == '<' && keystring
[6] == '>')
384 switch (toupper(keystring
[2])) {
386 search_key
.mask
= GDK_SHIFT_MASK
;
387 keystring
[4] = toupper(keystring
[4]);
390 search_key
.mask
= GDK_CONTROL_MASK
;
393 search_key
.mask
= get_modkey(keystring
[3]);
396 if (!search_key
.mask
)
398 search_key
.modkey
= keystring
[0];
399 if (strlen(keystring
) == 6) {
400 search_key
.key
= keystring
[4];
402 search_key
.key
= keystring
[5];
405 return (changemapping(&search_key
, maprecord
, cmd
));
409 process_map_line(char *line
) {
415 if (!strlen(my_pair
.what
))
417 while (isspace(*c
) && *c
)
420 if (*c
== ':' || *c
== '=')
424 if (!strlen(my_pair
.value
))
426 listlen
= LENGTH(commands
);
427 for (i
= 0; i
< listlen
; i
++) {
428 /* commands is fixed size */
429 if (commands
[i
].cmd
== NULL
)
431 if (strlen(commands
[i
].cmd
) == strlen(my_pair
.value
) && strncmp(commands
[i
].cmd
, my_pair
.value
, strlen(my_pair
.value
)) == 0) {
432 /* map to an internal symbol */
433 return process_mapping(my_pair
.what
, i
, NULL
);
436 /* if this is reached, the mapping is not for one of the internal symbol - test for command line structure */
437 if (strlen(my_pair
.value
) > 1 && strncmp(my_pair
.value
, ":", 1) == 0) {
438 /* The string begins with a colon, like a command line, but it's not _just_ a colon,
439 * i.e. increasing the pointer by one will not go 'out of bounds'.
440 * We don't actually check that the command line after the = is valid.
441 * This is user responsibility, the worst case is the new mapping simply doing nothing.
442 * Since we will pass the command to the same function which also handles the config file lines,
443 * we have to strip the colon itself (a colon counts as a commented line there - like in vim).
444 * Last, but not least, the second argument being < 0 signifies to the function that this is a
445 * command line mapping, not a mapping to an existing internal symbol. */
446 cmd
= (char *)malloc(sizeof(char) * strlen(my_pair
.value
));
447 strncpy(cmd
, (my_pair
.value
+ 1), strlen(my_pair
.value
) - 1);
448 cmd
[strlen(cmd
)] = '\0';
449 return process_mapping(my_pair
.what
, -1, cmd
);
455 build_taglist(const Arg
*arg
, FILE *f
) {
456 int k
= 0, in_tag
= 0;
457 int t
= 0, marker
= 0;
458 char foundtab
[MAXTAGSIZE
+1];
460 if (!isspace(arg
->s
[k
]) && !in_tag
) {
464 if (isspace(arg
->s
[k
]) && in_tag
) {
467 while (marker
< k
&& t
< MAXTAGSIZE
) foundtab
[t
++] = arg
->s
[marker
++];
469 fprintf(f
, " [%s]", foundtab
);
476 while (marker
< strlen(arg
->s
) && t
< MAXTAGSIZE
) foundtab
[t
++] = arg
->s
[marker
++];
478 fprintf(f
, " [%s]", foundtab
);
484 set_error(const char *error
) {
485 /* it should never happen that set_error is called more than once,
486 * but to avoid any potential memory leaks, we ignore any subsequent
487 * error if the current one has not been shown */
488 if (error_msg
== NULL
) {
489 error_msg
= g_strdup_printf("%s", error
);
494 give_feedback(const char *feedback
)
496 Arg a
= { .i
= Info
};
498 a
.s
= g_strdup_printf("%s", feedback
);
504 complete_list(const char *searchfor
, const int mode
, Listelement
*elementlist
)
507 const char *filename
;
508 Listelement
*candidatelist
= NULL
, *candidatepointer
= NULL
;
509 char s
[255] = "", readelement
[MAXTAGSIZE
+ 1] = "";
513 /* open in history file */
514 filename
= g_strdup_printf(HISTORY_STORAGE_FILENAME
);
516 /* open in bookmark file (for tags and bookmarks) */
517 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
519 f
= fopen(filename
, "r");
521 g_free((gpointer
*)filename
);
522 return (elementlist
);
525 while (fgets(s
, 254, f
)) {
527 /* just tags (could be more than one per line) */
529 while (s
[i
] && i
< 254) {
530 while (s
[i
] != '[' && s
[i
])
536 while (s
[i
] != ']' && s
[i
] && t
< MAXTAGSIZE
)
537 readelement
[t
++] = s
[i
++];
538 readelement
[t
] = '\0';
539 candidatelist
= add_list(readelement
, candidatelist
);
543 /* complete string (bookmarks & history) */
544 candidatelist
= add_list(s
, candidatelist
);
546 candidatepointer
= candidatelist
;
547 while (candidatepointer
!= NULL
) {
548 strncpy(s
, candidatepointer
->element
, sizeof(s
));
549 if (!complete_case_sensitive
) {
552 if (!strlen(searchfor
) || strstr(s
, searchfor
) != NULL
) {
553 /* only use string up to the first space */
554 memset(readelement
, 0, MAXTAGSIZE
+ 1);
555 if (strchr(candidatepointer
->element
, ' ') != NULL
) {
556 i
= strcspn(candidatepointer
->element
, " ");
559 strncpy(readelement
, candidatepointer
->element
, i
);
561 strncpy(readelement
, candidatepointer
->element
, MAXTAGSIZE
);
563 /* in the case of URLs without title, remove the line break */
564 if (readelement
[strlen(readelement
) - 1] == '\n') {
565 readelement
[strlen(readelement
) - 1] = '\0';
567 elementlist
= add_list(readelement
, elementlist
);
568 n
= count_list(elementlist
);
570 if (n
>= MAX_LIST_SIZE
)
572 candidatepointer
= candidatepointer
->next
;
574 free_list(candidatelist
);
575 candidatelist
= NULL
;
576 if (n
>= MAX_LIST_SIZE
)
579 g_free((gpointer
)filename
);
580 return (elementlist
);
584 add_list(const char *element
, Listelement
*elementlist
)
587 Listelement
*newelement
, *elementpointer
, *lastelement
;
589 if (elementlist
== NULL
) { /* first element */
590 newelement
= malloc(sizeof(Listelement
));
591 if (newelement
== NULL
)
592 return (elementlist
);
593 strncpy(newelement
->element
, element
, 254);
594 newelement
->next
= NULL
;
597 elementpointer
= elementlist
;
600 /* check if element is already in list */
601 while (elementpointer
!= NULL
) {
602 if (strlen(elementpointer
->element
) == n
&&
603 strncmp(elementpointer
->element
, element
, n
) == 0)
604 return (elementlist
);
605 lastelement
= elementpointer
;
606 elementpointer
= elementpointer
->next
;
610 newelement
= malloc(sizeof(Listelement
));
611 if (newelement
== NULL
)
612 return (elementlist
);
613 lastelement
->next
= newelement
;
614 strncpy(newelement
->element
, element
, 254);
615 newelement
->next
= NULL
;
620 free_list(Listelement
*elementlist
)
622 Listelement
*elementpointer
;
624 while (elementlist
!= NULL
) {
625 elementpointer
= elementlist
->next
;
627 elementlist
= elementpointer
;
632 count_list(Listelement
*elementlist
)
634 Listelement
*elementpointer
= elementlist
;
637 while (elementpointer
!= NULL
) {
639 elementpointer
= elementpointer
->next
;
645 /* split the string at the first occurence of whitespace and return the
646 * position of the second half.
647 * Unlike strtok, the substrings can be empty and the second string is
648 * stripped of trailing and leading whitespace.
649 * Return -1 if `string' contains no whitespace */
650 static int split_string_at_whitespace(char *string
)
652 int index
= strcspn(string
, "\n\t ");
653 if (string
[index
] != '\0') {
655 g_strstrip(string
+index
);
661 /* return TRUE, if the string contains exactly one unescaped %s and no other
662 * printf directives */
663 static gboolean
sanity_check_search_url(const char *string
)
665 int was_percent_char
= 0, percent_s_count
= 0;
667 for (; *string
; string
++) {
670 was_percent_char
= !was_percent_char
;
673 if (was_percent_char
)
675 was_percent_char
= 0;
678 if (was_percent_char
)
680 was_percent_char
= 0;
685 return !was_percent_char
&& percent_s_count
== 1;
688 void make_searchengines_list(Searchengine
*searchengines
, int length
)
691 for (i
= 0; i
< length
; i
++, searchengines
++) {
692 dynamic_searchengines
= g_list_prepend(dynamic_searchengines
, searchengines
);
696 /* find a searchengine with a given handle and return its URI or NULL if
698 * The returned string is internal and must not be freed or modified. */
699 char *find_uri_for_searchengine(const char *handle
)
703 if (dynamic_searchengines
!= NULL
) {
704 for (l
= dynamic_searchengines
; l
; l
= g_list_next(l
)) {
705 Searchengine
*s
= (Searchengine
*)l
->data
;
706 if (!strcmp(s
->handle
, handle
)) {
716 read_rcfile(const char *config
)
718 int t
, linum
= 0, index
;
719 char s
[255], *buffer
;
721 gboolean found_malformed_lines
= FALSE
;
724 if (access(config
, F_OK
) != 0)
725 return FILE_NOT_FOUND
;
727 fpin
= fopen(config
, "r");
729 return READING_FAILED
;
731 while (fgets(s
, 254, fpin
)) {
734 * ignore lines that begin with #, / and such
740 if (strncmp(s
, "searchengine", 12) == 0) {
742 while (buffer
[0] == ' ')
744 /* split line at whitespace */
745 index
= split_string_at_whitespace(buffer
);
746 if (index
< 0 || buffer
[0] == '\0' || buffer
[index
] == '\0'
747 || !sanity_check_search_url(buffer
+index
)) {
748 fprintf(stderr
, "searchengines: syntax error on line %d\n", linum
);
749 found_malformed_lines
= TRUE
;
752 new = malloc(sizeof(Searchengine
));
754 fprintf(stderr
, "Memory exhausted while loading search engines.\n");
757 new->handle
= g_strdup(buffer
);
758 new->uri
= g_strdup(buffer
+index
);
759 dynamic_searchengines
= g_list_prepend(dynamic_searchengines
, new);
761 if (!process_line(s
))
762 found_malformed_lines
= TRUE
;
766 return found_malformed_lines
? SYNTAX_ERROR
: SUCCESS
;