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
11 #include "vimprobable.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
;
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
)
32 gboolean returnval
= TRUE
;
34 if ((fpin
= fopen(config
, "r")) == NULL
)
36 while (fgets(s
, 254, fpin
)) {
38 * ignore lines that begin with #, / and such
51 void save_command_history(char *line
)
56 while (isspace(*c
) && *c
)
60 strncpy(commandhistory
[lastcommand
], ":", 1);
61 strncat(commandhistory
[lastcommand
], c
, 254);
63 if (maxcommands
< COMMANDHISTSIZE
- 1)
65 if (lastcommand
== COMMANDHISTSIZE
)
67 commandpointer
= lastcommand
;
71 process_save_qmark(const char *bm
, WebKitWebView
*webview
)
75 const char *uri
= webkit_web_view_get_uri(webview
);
82 if ( mark
< 1 || mark
> 9 )
85 a
.s
= g_strdup_printf("Invalid quickmark, only 1-9");
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");
99 for( i
=0; i
< 10; ++i
) {
105 while (buf
[l
] && l
< 100 && buf
[l
] != '\n') {
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
]);
123 a
.s
= g_strdup_printf("Saved as quickmark %d: %s", mark
, uri
);
134 KeyList
*ptr
, *current
;
139 while ( keys
[i
].key
!= 0 )
141 current
= malloc(sizeof(KeyList
));
142 if (current
== NULL
) {
143 printf("Not enough memory\n");
146 current
->Element
= keys
[i
];
147 current
->next
= NULL
;
148 if (keylistroot
== NULL
) keylistroot
= current
;
149 if (ptr
!= NULL
) ptr
->next
= current
;
156 parse_colour(char *color
) {
160 colorlen
= (int)strlen(color
);
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] == '#') {
173 strncpy(goodcolor
, color
, 7);
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];
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];
195 strncpy(&goodcolor
[1], color
, 6);
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];
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];
216 if (strlen (goodcolor
) != 7) {
219 strncpy(color
, goodcolor
, 8);
225 process_line_arg(const Arg
*arg
) {
226 return process_line(arg
->s
);
230 changemapping(Key
*search_key
, int maprecord
, char *cmd
) {
231 KeyList
*current
, *newkey
;
232 Arg a
= { .s
= cmd
};
235 if (maprecord
< 0 && cmd
== NULL
) {
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 */
244 current
= keylistroot
;
247 while (current
->next
!= NULL
) {
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
;
258 /* mapping to a command line */
259 current
->Element
.func
= process_line_arg
;
260 current
->Element
.arg
= a
;
264 current
= current
->next
;
266 newkey
= malloc(sizeof(KeyList
));
267 if (newkey
== NULL
) {
268 printf("Not enough memory\n");
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
;
279 /* mapping to a command line */
280 newkey
->Element
.func
= process_line_arg
;
281 newkey
->Element
.arg
= a
;
283 add_modkeys(newkey
->Element
.modkey
);
287 if (keylistroot
== NULL
) keylistroot
= newkey
;
289 if (current
!= NULL
) current
->next
= newkey
;
294 void add_modkeys(char key
)
297 extern char *modkeys
;
298 len
= strlen( modkeys
);
301 if ( modkeys
[k
] == key
) return;
304 modkeys
= realloc(modkeys
, len
+ 1);
305 modkeys
[len
++] = key
;
310 mappings(const Arg
*arg
) {
314 set_error("Missing argument.");
317 strncpy(line
, arg
->s
, 254);
318 if (process_map_line(line
))
321 set_error("Invalid mapping.");
327 get_modkey(char key
) {
330 return GDK_MOD1_MASK
;
332 return GDK_MOD2_MASK
;
334 return GDK_MOD3_MASK
;
336 return GDK_MOD4_MASK
;
338 return GDK_MOD5_MASK
;
345 process_mapping(char *keystring
, int maprecord
, char *cmd
) {
349 search_key
.modkey
= 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)
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])) {
372 search_key
.mask
= GDK_SHIFT_MASK
;
373 if (strlen(keystring
) == 5) {
374 keystring
[3] = toupper(keystring
[3]);
376 keystring
[3] = tolower(keystring
[3]);
377 keystring
[5] = toupper(keystring
[5]);
381 search_key
.mask
= GDK_CONTROL_MASK
;
384 search_key
.mask
= get_modkey(keystring
[2]);
387 if (!search_key
.mask
)
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];
395 if (search_key
.mask
== 'S' || search_key
.mask
== 'C') {
396 search_key
.modkey
= keystring
[3];
397 search_key
.key
= keystring
[5];
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)
408 (strlen(keystring
) == 6 && keystring
[1] == '<' && keystring
[5] == '>') ||
409 (strlen(keystring
) == 7 && keystring
[1] == '<' && keystring
[6] == '>')
411 switch (toupper(keystring
[2])) {
413 search_key
.mask
= GDK_SHIFT_MASK
;
414 keystring
[4] = toupper(keystring
[4]);
417 search_key
.mask
= GDK_CONTROL_MASK
;
420 search_key
.mask
= get_modkey(keystring
[3]);
423 if (!search_key
.mask
)
425 search_key
.modkey
= keystring
[0];
426 if (strlen(keystring
) == 6) {
427 search_key
.key
= keystring
[4];
429 search_key
.key
= keystring
[5];
432 return (changemapping(&search_key
, maprecord
, cmd
));
436 process_map_line(char *line
) {
442 if (!strlen(my_pair
.what
))
444 while (isspace(*c
) && *c
)
447 if (*c
== ':' || *c
== '=')
451 if (!strlen(my_pair
.value
))
453 listlen
= LENGTH(commands
);
454 for (i
= 0; i
< listlen
; i
++) {
455 /* commands is fixed size */
456 if (commands
[i
].cmd
== NULL
)
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
);
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];
487 if (!isspace(arg
->s
[k
]) && !in_tag
) {
491 if (isspace(arg
->s
[k
]) && in_tag
) {
494 while (marker
< k
&& t
< MAXTAGSIZE
) foundtab
[t
++] = arg
->s
[marker
++];
496 fprintf(f
, " [%s]", foundtab
);
503 while (marker
< strlen(arg
->s
) && t
< MAXTAGSIZE
) foundtab
[t
++] = arg
->s
[marker
++];
505 fprintf(f
, " [%s]", foundtab
);
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
);
521 give_feedback(const char *feedback
)
523 Arg a
= { .i
= Info
};
525 a
.s
= g_strdup_printf("%s", feedback
);
531 complete_list(const char *searchfor
, const int mode
, Listelement
*elementlist
)
534 const char *filename
;
535 Listelement
*candidatelist
= NULL
, *candidatepointer
= NULL
;
536 char s
[255] = "", readelement
[MAXTAGSIZE
+ 1] = "";
540 /* open in history file */
541 filename
= g_strdup_printf(HISTORY_STORAGE_FILENAME
);
543 /* open in bookmark file (for tags and bookmarks) */
544 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
546 f
= fopen(filename
, "r");
548 g_free((gpointer
*)filename
);
549 return (elementlist
);
552 while (fgets(s
, 254, f
)) {
554 /* just tags (could be more than one per line) */
556 while (s
[i
] && i
< 254) {
557 while (s
[i
] != '[' && s
[i
])
563 while (s
[i
] != ']' && s
[i
] && t
< MAXTAGSIZE
)
564 readelement
[t
++] = s
[i
++];
565 readelement
[t
] = '\0';
566 candidatelist
= add_list(readelement
, candidatelist
);
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
) {
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
, " ");
586 strncpy(readelement
, candidatepointer
->element
, i
);
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
)
599 candidatepointer
= candidatepointer
->next
;
601 free_list(candidatelist
);
602 candidatelist
= NULL
;
603 if (n
>= MAX_LIST_SIZE
)
606 g_free((gpointer
)filename
);
607 return (elementlist
);
611 add_list(const char *element
, Listelement
*elementlist
)
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
;
624 elementpointer
= elementlist
;
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
;
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
;
647 free_list(Listelement
*elementlist
)
649 Listelement
*elementpointer
;
651 while (elementlist
!= NULL
) {
652 elementpointer
= elementlist
->next
;
654 elementlist
= elementpointer
;
659 count_list(Listelement
*elementlist
)
661 Listelement
*elementpointer
= elementlist
;
664 while (elementpointer
!= NULL
) {
666 elementpointer
= elementpointer
->next
;
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') {
682 g_strstrip(string
+index
);
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
++) {
697 was_percent_char
= !was_percent_char
;
700 if (was_percent_char
)
702 was_percent_char
= 0;
705 if (was_percent_char
)
707 was_percent_char
= 0;
712 return !was_percent_char
&& percent_s_count
== 1;
716 read_searchengines(const char *filename
)
719 char buffer
[BUFFERSIZE
], c
;
720 int linum
= 0, index
;
721 gboolean found_malformed_lines
= FALSE
;
724 if (access(filename
, F_OK
) != 0)
725 return FILE_NOT_FOUND
;
727 file
= fopen(filename
, "r");
729 return READING_FAILED
;
731 while (fgets(buffer
, BUFFERSIZE
, file
)) {
734 /* skip empty lines */
735 if (!strcmp(buffer
, "\n")) continue;
737 /* skip too long lines */
738 if (buffer
[strlen(buffer
)-1] != '\n') {
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
;
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
;
758 new = malloc(sizeof(Searchengine
));
760 fprintf(stderr
, "Memory exhausted while loading search engines.\n");
764 new->handle
= g_strdup(buffer
);
765 new->uri
= g_strdup(buffer
+index
);
767 dynamic_searchengines
= g_list_prepend(dynamic_searchengines
, new);
772 return READING_FAILED
;
777 return found_malformed_lines
? SYNTAX_ERROR
: SUCCESS
;
780 void make_searchengines_list(Searchengine
*searchengines
, int length
)
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
790 * The returned string is internal and must not be freed or modified. */
791 char *find_uri_for_searchengine(const char *handle
)
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
)) {