rofi 1.7.9
helper.c
Go to the documentation of this file.
1/*
2 * rofi
3 *
4 * MIT/X11 License
5 * Copyright © 2012 Sean Pringle <sean.pringle@gmail.com>
6 * Copyright © 2013-2023 Qball Cow <qball@gmpclient.org>
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining
9 * a copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be
17 * included in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 */
28
30#include "config.h"
31#define G_LOG_DOMAIN "Helper"
32
33#include "display.h"
34#include "helper-theme.h"
35#include "helper.h"
36#include "rofi.h"
37#include "settings.h"
38#include "view.h"
39#include "xcb.h"
40#include <ctype.h>
41#include <errno.h>
42#include <fcntl.h>
43#include <glib.h>
44#include <glib/gstdio.h>
45#include <limits.h>
46#include <pango/pango-fontmap.h>
47#include <pango/pango.h>
48#include <pango/pangocairo.h>
49#include <pwd.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <sys/file.h>
54#include <sys/stat.h>
55#include <sys/types.h>
56#include <unistd.h>
57
58const char *const MatchingMethodStr[MM_NUM_MATCHERS] = {
59 "Normal", "Regex", "Glob", "Fuzzy", "Prefix"};
60
63 -1,
64};
66static int CurrentMatchingMethod = 0;
67
71const char *const monitor_position_entries[] = {
72 "on focused monitor", "on focused window", "at mouse pointer",
73 "on monitor with focused window", "on monitor that has mouse pointer"};
74
77char **stored_argv = NULL;
78
79char *helper_string_replace_if_exists_v(char *string, GHashTable *h);
80
82 return MatchingMethodStr[config.matching_method];
83}
97
98void cmd_set_arguments(int argc, char **argv) {
99 stored_argc = argc;
100 stored_argv = argv;
101}
102
103int helper_parse_setup(char *string, char ***output, int *length, ...) {
104 GError *error = NULL;
105 GHashTable *h;
106 h = g_hash_table_new(g_str_hash, g_str_equal);
107 // By default, we insert terminal and ssh-client
108 g_hash_table_insert(h, "{terminal}", config.terminal_emulator);
109 g_hash_table_insert(h, "{ssh-client}", config.ssh_client);
110 // Add list from variable arguments.
111 va_list ap;
112 va_start(ap, length);
113 while (1) {
114 char *key = va_arg(ap, char *);
115 if (key == (char *)0) {
116 break;
117 }
118 char *value = va_arg(ap, char *);
119 if (value == (char *)0) {
120 break;
121 }
122 g_hash_table_insert(h, key, value);
123 }
124 va_end(ap);
125
126 char *res = helper_string_replace_if_exists_v(string, h);
127 // Destroy key-value storage.
128 g_hash_table_destroy(h);
129 // Parse the string into shell arguments.
130 if (g_shell_parse_argv(res, length, output, &error)) {
131 g_free(res);
132 return TRUE;
133 }
134 g_free(res);
135 // Throw error if shell parsing fails.
136 if (error) {
137 char *msg = g_strdup_printf("Failed to parse: '%s'\nError: '%s'", string,
138 error->message);
139 rofi_view_error_dialog(msg, FALSE);
140 g_free(msg);
141 // print error.
142 g_error_free(error);
143 }
144 return FALSE;
145}
146
148 for (size_t i = 0; tokens && tokens[i]; i++) {
149 g_regex_unref((GRegex *)tokens[i]->regex);
150 g_free(tokens[i]);
151 }
152 g_free(tokens);
153}
154
155static gchar *glob_to_regex(const char *input) {
156 gchar *r = g_regex_escape_string(input, -1);
157 size_t str_l = strlen(r);
158 for (size_t i = 0; i < str_l; i++) {
159 if (r[i] == '\\') {
160 if (r[i + 1] == '*') {
161 r[i] = '.';
162 } else if (r[i + 1] == '?') {
163 r[i + 1] = 'S';
164 }
165 i++;
166 }
167 }
168 return r;
169}
170static gchar *fuzzy_to_regex(const char *input) {
171 char *retv = NULL;
172 GString *str = g_string_new("");
173 gchar *r = g_regex_escape_string(input, -1);
174 gchar *iter;
175 int first = 1;
176 for (iter = r; iter && *iter != '\0'; iter = g_utf8_next_char(iter)) {
177 if (first) {
178 g_string_append(str, "(");
179 } else {
180 g_string_append(str, ".*?(");
181 }
182 if (*iter == '\\') {
183 g_string_append_c(str, '\\');
184 iter = g_utf8_next_char(iter);
185 // If EOL, break out of for loop.
186 if ((*iter) == '\0') {
187 break;
188 }
189 }
190 g_string_append_unichar(str, g_utf8_get_char(iter));
191 g_string_append(str, ")");
192 first = 0;
193 }
194 g_free(r);
195 retv = g_string_free(str, FALSE);
196 return retv;
197}
198
199static gchar *prefix_regex(const char *input) {
200 gchar *r = g_regex_escape_string(input, -1);
201 char *retv = g_strconcat("\\b", r, NULL);
202 g_free(r);
203 return retv;
204}
205
206static char *utf8_helper_simplify_string(const char *os) {
207 char buf[6] = {
208 0,
209 };
210
211 // Normalize the string to a fully decomposed form, then filter out
212 // mark/accent characters.
213 char *s = g_utf8_normalize(os, -1, G_NORMALIZE_ALL);
214 ssize_t str_size = (g_utf8_strlen(s, -1) * 6 + 2 + 1) * sizeof(char);
215 char *str = g_malloc0(str_size);
216 char *striter = str;
217 for (const char *iter = s; iter && *iter; iter = g_utf8_next_char(iter)) {
218 gunichar uc = g_utf8_get_char(iter);
219 if (!g_unichar_ismark(uc)) {
220 int l = g_unichar_to_utf8(uc, buf);
221 memcpy(striter, buf, l);
222 striter += l;
223 }
224 }
225 g_free(s);
226
227 return str;
228}
229
230// Macro for quickly generating regex for matching.
231static inline GRegex *R(const char *s, int case_sensitive) {
232 if (config.normalize_match) {
233 char *str = utf8_helper_simplify_string(s);
234
235 GRegex *r = g_regex_new(
236 str, G_REGEX_OPTIMIZE | ((case_sensitive) ? 0 : G_REGEX_CASELESS), 0,
237 NULL);
238
239 g_free(str);
240 return r;
241 }
242 return g_regex_new(
243 s, G_REGEX_OPTIMIZE | ((case_sensitive) ? 0 : G_REGEX_CASELESS), 0, NULL);
244}
245
246static rofi_int_matcher *create_regex(const char *input, int case_sensitive) {
247 GRegex *retv = NULL;
248 gchar *r;
249 rofi_int_matcher *rv = g_malloc0(sizeof(rofi_int_matcher));
250 if (input && input[0] == config.matching_negate_char) {
251 rv->invert = 1;
252 input++;
253 }
254 switch (config.matching_method) {
255 case MM_GLOB:
256 r = glob_to_regex(input);
257 retv = R(r, case_sensitive);
258 g_free(r);
259 break;
260 case MM_REGEX:
261 retv = R(input, case_sensitive);
262 if (retv == NULL) {
263 r = g_regex_escape_string(input, -1);
264 retv = R(r, case_sensitive);
265 g_free(r);
266 }
267 break;
268 case MM_FUZZY:
269 r = fuzzy_to_regex(input);
270 retv = R(r, case_sensitive);
271 g_free(r);
272 break;
273 case MM_PREFIX:
274 r = prefix_regex(input);
275 retv = R(r, case_sensitive);
276 g_free(r);
277 break;
278 default:
279 r = g_regex_escape_string(input, -1);
280 retv = R(r, case_sensitive);
281 g_free(r);
282 break;
283 }
284 rv->regex = retv;
285 return rv;
286}
287rofi_int_matcher **helper_tokenize(const char *input, int case_sensitive) {
288 if (input == NULL) {
289 return NULL;
290 }
291 size_t len = strlen(input);
292 if (len == 0) {
293 return NULL;
294 }
295
296 char *saveptr = NULL, *token;
297 rofi_int_matcher **retv = NULL;
298 if (!config.tokenize) {
299 retv = g_malloc0(sizeof(rofi_int_matcher *) * 2);
300 retv[0] = create_regex(input, case_sensitive);
301 return retv;
302 }
303
304 // First entry is always full (modified) stringtext.
305 int num_tokens = 0;
306
307 // Copy the string, 'strtok_r' modifies it.
308 char *str = g_strdup(input);
309
310 // Iterate over tokens.
311 // strtok should still be valid for utf8.
312 const char *const sep = " ";
313 for (token = strtok_r(str, sep, &saveptr); token != NULL;
314 token = strtok_r(NULL, sep, &saveptr)) {
315 retv = g_realloc(retv, sizeof(rofi_int_matcher *) * (num_tokens + 2));
316 retv[num_tokens] = create_regex(token, case_sensitive);
317 retv[num_tokens + 1] = NULL;
318 num_tokens++;
319 }
320 // Free str.
321 g_free(str);
322 return retv;
323}
324
325// cli arg handling
326int find_arg(const char *const key) {
327 int i;
328
329 for (i = 0; i < stored_argc && strcasecmp(stored_argv[i], key); i++) {
330 ;
331 }
332
333 return i < stored_argc ? i : -1;
334}
335int find_arg_str(const char *const key, char **val) {
336 int i = find_arg(key);
337
338 if (val != NULL && i > 0 && i < stored_argc - 1) {
339 *val = stored_argv[i + 1];
340 return TRUE;
341 }
342 return FALSE;
343}
344
345const char **find_arg_strv(const char *const key) {
346 const char **retv = NULL;
347 int length = 0;
348 for (int i = 0; i < stored_argc; i++) {
349 if (i < (stored_argc - 1) && strcasecmp(stored_argv[i], key) == 0) {
350 length++;
351 }
352 }
353 if (length > 0) {
354 retv = g_malloc0((length + 1) * sizeof(char *));
355 int index = 0;
356 for (int i = 0; i < stored_argc; i++) {
357 if (i < (stored_argc - 1) && strcasecmp(stored_argv[i], key) == 0) {
358 retv[index++] = stored_argv[i + 1];
359 }
360 }
361 }
362 return retv;
363}
364
365int find_arg_int(const char *const key, int *val) {
366 int i = find_arg(key);
367
368 if (val != NULL && i > 0 && i < (stored_argc - 1)) {
369 *val = strtol(stored_argv[i + 1], NULL, 10);
370 return TRUE;
371 }
372 return FALSE;
373}
374int find_arg_uint(const char *const key, unsigned int *val) {
375 int i = find_arg(key);
376
377 if (val != NULL && i > 0 && i < (stored_argc - 1)) {
378 *val = strtoul(stored_argv[i + 1], NULL, 10);
379 return TRUE;
380 }
381 return FALSE;
382}
383
384char helper_parse_char(const char *arg) {
385 const size_t len = strlen(arg);
386 // If the length is 1, it is not escaped.
387 if (len == 1) {
388 return arg[0];
389 }
390 // If the length is 2 and the first character is '\', we unescape it.
391 if (len == 2 && arg[0] == '\\') {
392 switch (arg[1]) {
393 // New line
394 case 'n':
395 return '\n';
396 // Bell
397 case 'a':
398 return '\a';
399 // Backspace
400 case 'b':
401 return '\b';
402 // Tab
403 case 't':
404 return '\t';
405 // Vertical tab
406 case 'v':
407 return '\v';
408 // Form feed
409 case 'f':
410 return '\f';
411 // Carriage return
412 case 'r':
413 return '\r';
414 // Forward slash
415 case '\\':
416 return '\\';
417 // 0 line.
418 case '0':
419 return '\0';
420 default:
421 break;
422 }
423 }
424 if (len > 2 && arg[0] == '\\' && arg[1] == 'x') {
425 return (char)strtol(&arg[2], NULL, 16);
426 }
427 g_warning("Failed to parse character string: \"%s\"", arg);
428 // for now default to newline.
429 return '\n';
430}
431
432int find_arg_char(const char *const key, char *val) {
433 int i = find_arg(key);
434
435 if (val != NULL && i > 0 && i < (stored_argc - 1)) {
436 *val = helper_parse_char(stored_argv[i + 1]);
437 return TRUE;
438 }
439 return FALSE;
440}
441
442void helper_token_match_set_pango_attr_on_style(PangoAttrList *retv, int start,
443 int end,
445 if (th.style & ROFI_HL_BOLD) {
446 PangoAttribute *pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
447 pa->start_index = start;
448 pa->end_index = end;
449 pango_attr_list_insert(retv, pa);
450 }
451#if PANGO_VERSION_CHECK(1, 50, 0)
452 if (th.style & ROFI_HL_UPPERCASE) {
453 PangoAttribute *pa =
454 pango_attr_text_transform_new(PANGO_TEXT_TRANSFORM_UPPERCASE);
455 pa->start_index = start;
456 pa->end_index = end;
457 pango_attr_list_insert(retv, pa);
458 }
459 if (th.style & ROFI_HL_LOWERCASE) {
460 PangoAttribute *pa =
461 pango_attr_text_transform_new(PANGO_TEXT_TRANSFORM_LOWERCASE);
462 pa->start_index = start;
463 pa->end_index = end;
464 pango_attr_list_insert(retv, pa);
465 }
466 if (th.style & ROFI_HL_CAPITALIZE) {
467#if 0
468 PangoAttribute *pa =
469 pango_attr_text_transform_new(PANGO_TEXT_TRANSFORM_CAPITALIZE);
470 pa->start_index = start;
471 pa->end_index = end;
472 pango_attr_list_insert(retv, pa);
473#endif
474 // Disabled because of bug in pango
475 }
476#endif
477 if (th.style & ROFI_HL_UNDERLINE) {
478 PangoAttribute *pa = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
479 pa->start_index = start;
480 pa->end_index = end;
481 pango_attr_list_insert(retv, pa);
482 }
483 if (th.style & ROFI_HL_STRIKETHROUGH) {
484 PangoAttribute *pa = pango_attr_strikethrough_new(TRUE);
485 pa->start_index = start;
486 pa->end_index = end;
487 pango_attr_list_insert(retv, pa);
488 }
489 if (th.style & ROFI_HL_ITALIC) {
490 PangoAttribute *pa = pango_attr_style_new(PANGO_STYLE_ITALIC);
491 pa->start_index = start;
492 pa->end_index = end;
493 pango_attr_list_insert(retv, pa);
494 }
495 if (th.style & ROFI_HL_COLOR) {
496 PangoAttribute *pa = pango_attr_foreground_new(
497 th.color.red * 65535, th.color.green * 65535, th.color.blue * 65535);
498 pa->start_index = start;
499 pa->end_index = end;
500 pango_attr_list_insert(retv, pa);
501
502 if (th.color.alpha < 1.0) {
503 pa = pango_attr_foreground_alpha_new(th.color.alpha * 65535);
504 pa->start_index = start;
505 pa->end_index = end;
506 pango_attr_list_insert(retv, pa);
507 }
508 }
509}
510
512 rofi_int_matcher **tokens,
513 const char *input,
514 PangoAttrList *retv) {
515 // Disable highlighting for normalize match, not supported atm.
516 if (config.normalize_match) {
517 return retv;
518 }
519 // Do a tokenized match.
520 if (tokens) {
521 for (int j = 0; tokens[j]; j++) {
522 GMatchInfo *gmi = NULL;
523 if (tokens[j]->invert) {
524 continue;
525 }
526 g_regex_match(tokens[j]->regex, input, G_REGEX_MATCH_PARTIAL, &gmi);
527 while (g_match_info_matches(gmi)) {
528 int count = g_match_info_get_match_count(gmi);
529 for (int index = (count > 1) ? 1 : 0; index < count; index++) {
530 int start, end;
531 g_match_info_fetch_pos(gmi, index, &start, &end);
532 helper_token_match_set_pango_attr_on_style(retv, start, end, th);
533 }
534 g_match_info_next(gmi, NULL);
535 }
536 g_match_info_free(gmi);
537 }
538 }
539 return retv;
540}
541
542int helper_token_match(rofi_int_matcher *const *tokens, const char *input) {
543 int match = TRUE;
544 // Do a tokenized match.
545 if (tokens) {
546 if (config.normalize_match) {
547 char *r = utf8_helper_simplify_string(input);
548 for (int j = 0; match && tokens[j]; j++) {
549 match = g_regex_match(tokens[j]->regex, r, 0, NULL);
550 match ^= tokens[j]->invert;
551 }
552 g_free(r);
553 } else {
554 for (int j = 0; match && tokens[j]; j++) {
555 match = g_regex_match(tokens[j]->regex, input, 0, NULL);
556 match ^= tokens[j]->invert;
557 }
558 }
559 }
560 return match;
561}
562
563int execute_generator(const char *cmd) {
564 char **args = NULL;
565 int argv = 0;
566 helper_parse_setup(config.run_command, &args, &argv, "{cmd}", cmd, (char *)0);
567
568 int fd = -1;
569 GError *error = NULL;
570 g_spawn_async_with_pipes(NULL, args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
571 NULL, NULL, &fd, NULL, &error);
572
573 if (error != NULL) {
574 char *msg = g_strdup_printf("Failed to execute: '%s'\nError: '%s'", cmd,
575 error->message);
576 rofi_view_error_dialog(msg, FALSE);
577 g_free(msg);
578 // print error.
579 g_error_free(error);
580 fd = -1;
581 }
582 g_strfreev(args);
583 return fd;
584}
585
586int create_pid_file(const char *pidfile, gboolean kill_running) {
587 if (pidfile == NULL) {
588 return -1;
589 }
590
591 int fd = g_open(pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
592 if (fd < 0) {
593 g_warning("Failed to create pid file: '%s'.", pidfile);
594 return -1;
595 }
596 // Set it to close the File Descriptor on exit.
597 int flags = fcntl(fd, F_GETFD, NULL);
598 flags = flags | FD_CLOEXEC;
599 if (fcntl(fd, F_SETFD, flags, NULL) < 0) {
600 g_warning("Failed to set CLOEXEC on pidfile.");
601 remove_pid_file(fd);
602 return -1;
603 }
604 // Try to get exclusive write lock on FD
605 int retv = flock(fd, LOCK_EX | LOCK_NB);
606 if (retv != 0) {
607 g_warning("Failed to set lock on pidfile: Rofi already running?");
608 g_warning("Got error: %d %s", retv, g_strerror(errno));
609 if (kill_running) {
610 char buffer[64] = {
611 0,
612 };
613 ssize_t l = read(fd, &(buffer[0]), 63);
614 if (l > 1) {
615 buffer[l] = 0;
616 pid_t pid = g_ascii_strtoll(buffer, NULL, 0);
617 kill(pid, SIGTERM);
618 while (1) {
619 retv = flock(fd, LOCK_EX | LOCK_NB);
620 if (retv == 0) {
621 break;
622 }
623 g_usleep(100);
624 }
625 }
626 remove_pid_file(fd);
627 return create_pid_file(pidfile, FALSE);
628 }
629
630 remove_pid_file(fd);
631 return -1;
632 }
633 if (ftruncate(fd, (off_t)0) == 0) {
634 // Write pid, not needed, but for completeness sake.
635 char buffer[64];
636 int length = snprintf(buffer, 64, "%i", getpid());
637 ssize_t l = 0;
638 while (l < length) {
639 l += write(fd, &buffer[l], length - l);
640 }
641 }
642 return fd;
643}
644
645void remove_pid_file(int fd) {
646 if (fd >= 0) {
647 if (close(fd)) {
648 g_warning("Failed to close pidfile: '%s'", g_strerror(errno));
649 }
650 }
651}
652
653gboolean helper_validate_font(PangoFontDescription *pfd, const char *font) {
654 const char *fam = pango_font_description_get_family(pfd);
655 int size = pango_font_description_get_size(pfd);
656 if (fam == NULL || size == 0) {
657 g_debug("Pango failed to parse font: '%s'", font);
658 g_debug("Got family: <b>%s</b> at size: <b>%d</b>", fam ? fam : "{unknown}",
659 size);
660 return FALSE;
661 }
662 return TRUE;
663}
664
673 int found_error = FALSE;
674 GString *msg =
675 g_string_new("<big><b>The configuration failed to validate:</b></big>\n");
676
677 if (config.sorting_method) {
678 if (g_strcmp0(config.sorting_method, "normal") == 0) {
679 config.sorting_method_enum = SORT_NORMAL;
680 } else if (g_strcmp0(config.sorting_method, "levenshtein") == 0) {
681 config.sorting_method_enum = SORT_NORMAL;
682 } else if (g_strcmp0(config.sorting_method, "fzf") == 0) {
683 config.sorting_method_enum = SORT_FZF;
684 } else {
685 g_string_append_printf(
686 msg,
687 "\t<b>config.sorting_method</b>=%s is not a valid sorting "
688 "strategy.\nValid options are: normal or fzf.\n",
689 config.sorting_method);
690 found_error = 1;
691 }
692 }
693
694 if (config.matching) {
695 char **strv = g_strsplit(config.matching, ",", 0);
696 if (strv) {
697 int matching_method_index = 0;
698 for (char **str = strv; *str && matching_method_index < MM_NUM_MATCHERS;
699 str++) {
700 gboolean found = FALSE;
701 for (unsigned i = 0;
702 i < MM_NUM_MATCHERS && matching_method_index < MM_NUM_MATCHERS;
703 i++) {
704 if (g_ascii_strcasecmp(*str, MatchingMethodStr[i]) == 0) {
705 MatchingMethodEnabled[matching_method_index] = i;
706 matching_method_index++;
707 NUMMatchingMethodEnabled = matching_method_index;
708 if (matching_method_index == MM_NUM_MATCHERS) {
709 found_error = 1;
710 g_string_append_printf(msg,
711 "\t<b>config.matching</b> = %s to many "
712 "matching options enabled.\n",
713 config.matching);
714 }
715 found = TRUE;
716 }
717 }
718 if (!found) {
719 g_string_append_printf(msg,
720 "\t<b>config.matching</b>=%s is not a valid "
721 "matching strategy.\nValid options are: glob, "
722 "regex, fuzzy, prefix or normal.\n",
723 *str);
724 found_error = 1;
725 }
726 }
727 config.matching_method = MatchingMethodEnabled[0];
728 g_strfreev(strv);
729 }
730 }
731
732 if (config.element_height < 1) {
733 g_string_append_printf(msg,
734 "\t<b>config.element_height</b>=%d is invalid. An "
735 "element needs to be at least 1 line high.\n",
736 config.element_height);
737 config.element_height = 1;
738 found_error = TRUE;
739 }
740 if (!(config.location >= 0 && config.location <= 8)) {
741 g_string_append_printf(msg,
742 "\t<b>config.location</b>=%d is invalid. Value "
743 "should be between %d and %d.\n",
744 config.location, 0, 8);
745 config.location = WL_CENTER;
746 found_error = 1;
747 }
748
749 // Check size
750 {
752 if (!monitor_active(&mon)) {
753 const char *name = config.monitor;
754 if (name && name[0] == '-') {
755 int index = name[1] - '0';
756 if (index < 5 && index > 0) {
757 name = monitor_position_entries[index - 1];
758 }
759 }
760 g_string_append_printf(
761 msg, "\t<b>config.monitor</b>=%s Could not find monitor.\n", name);
762 found_error = TRUE;
763 }
764 }
765
766 if (g_strcmp0(config.monitor, "-3") == 0) {
767 // On -3, set to location 1.
768 config.location = 1;
769 }
770
771 if (found_error) {
772 g_string_append(msg, "Please update your configuration.");
774 return TRUE;
775 }
776
777 g_string_free(msg, TRUE);
778 return FALSE;
779}
780
781char *rofi_expand_path(const char *input) {
782 char **str = g_strsplit(input, G_DIR_SEPARATOR_S, -1);
783 for (unsigned int i = 0; str && str[i]; i++) {
784 // Replace ~ with current user homedir.
785 if (str[i][0] == '~' && str[i][1] == '\0') {
786 g_free(str[i]);
787 str[i] = g_strdup(g_get_home_dir());
788 }
789 // If other user, ask getpwnam.
790 else if (str[i][0] == '~') {
791 struct passwd *p = getpwnam(&(str[i][1]));
792 if (p != NULL) {
793 g_free(str[i]);
794 str[i] = g_strdup(p->pw_dir);
795 }
796 } else if (i == 0) {
797 char *s = str[i];
798 if (input[0] == G_DIR_SEPARATOR) {
799 str[i] = g_strdup_printf("%s%s", G_DIR_SEPARATOR_S, s);
800 g_free(s);
801 }
802 }
803 }
804 char *retv = g_build_filenamev(str);
805 g_strfreev(str);
806 return retv;
807}
808
810#define MIN3(a, b, c) \
811 ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c)))
812
813unsigned int levenshtein(const char *needle, const glong needlelen,
814 const char *haystack, const glong haystacklen,
815 int case_sensitive) {
816 if (needlelen == G_MAXLONG) {
817 // String to long, we cannot handle this.
818 return UINT_MAX;
819 }
820 unsigned int column[needlelen + 1];
821 for (glong y = 0; y < needlelen; y++) {
822 column[y] = y;
823 }
824 // Removed out of the loop, otherwise static code analyzers think it is
825 // unset.. silly but true. old loop: for ( glong y = 0; y <= needlelen; y++)
826 column[needlelen] = needlelen;
827 for (glong x = 1; x <= haystacklen; x++) {
828 const char *needles = needle;
829 column[0] = x;
830 gunichar haystackc = g_utf8_get_char(haystack);
831 if (!case_sensitive) {
832 haystackc = g_unichar_tolower(haystackc);
833 }
834 for (glong y = 1, lastdiag = x - 1; y <= needlelen; y++) {
835 gunichar needlec = g_utf8_get_char(needles);
836 if (!case_sensitive) {
837 needlec = g_unichar_tolower(needlec);
838 }
839 unsigned int olddiag = column[y];
840 column[y] = MIN3(column[y] + 1, column[y - 1] + 1,
841 lastdiag + (needlec == haystackc ? 0 : 1));
842 lastdiag = olddiag;
843 needles = g_utf8_next_char(needles);
844 }
845 haystack = g_utf8_next_char(haystack);
846 }
847 return column[needlelen];
848}
849
850char *rofi_latin_to_utf8_strdup(const char *input, gssize length) {
851 gsize slength = 0;
852 return g_convert_with_fallback(input, length, "UTF-8", "latin1", "\uFFFD",
853 NULL, &slength, NULL);
854}
855
856char *rofi_force_utf8(const gchar *data, ssize_t length) {
857 if (data == NULL) {
858 return NULL;
859 }
860 const char *end;
861 GString *string;
862
863 if (g_utf8_validate(data, length, &end)) {
864 return g_memdup2(data, length + 1);
865 }
866 string = g_string_sized_new(length + 16);
867
868 do {
869 /* Valid part of the string */
870 g_string_append_len(string, data, end - data);
871 /* Replacement character */
872 g_string_append(string, "\uFFFD");
873 length -= (end - data) + 1;
874 data = end + 1;
875 } while (!g_utf8_validate(data, length, &end));
876
877 if (length) {
878 g_string_append_len(string, data, length);
879 }
880
881 return g_string_free(string, FALSE);
882}
883
884/****
885 * FZF like scorer
886 */
887
889#define FUZZY_SCORER_MAX_LENGTH 256
891#define MIN_SCORE (INT_MIN / 2)
893#define LEADING_GAP_SCORE -4
895#define GAP_SCORE -5
897#define WORD_START_SCORE 50
899#define NON_WORD_SCORE 40
901#define CAMEL_SCORE (WORD_START_SCORE + GAP_SCORE - 1)
903#define CONSECUTIVE_SCORE (WORD_START_SCORE + GAP_SCORE)
905#define PATTERN_NON_START_MULTIPLIER 1
907#define PATTERN_START_MULTIPLIER 2
908
913 /* Lower case */
915 /* Upper case */
917 /* Number */
919 /* non word character */
921};
922
929 if (g_unichar_islower(c)) {
930 return LOWER;
931 }
932 if (g_unichar_isupper(c)) {
933 return UPPER;
934 }
935 if (g_unichar_isdigit(c)) {
936 return DIGIT;
937 }
938 return NON_WORD;
939}
940
949static int rofi_scorer_get_score_for(enum CharClass prev, enum CharClass curr) {
950 if (prev == NON_WORD && curr != NON_WORD) {
951 return WORD_START_SCORE;
952 }
953 if ((prev == LOWER && curr == UPPER) || (prev != DIGIT && curr == DIGIT)) {
954 return CAMEL_SCORE;
955 }
956 if (curr == NON_WORD) {
957 return NON_WORD_SCORE;
958 }
959 return 0;
960}
961
962int rofi_scorer_fuzzy_evaluate(const char *pattern, glong plen, const char *str,
963 glong slen, int case_sensitive) {
964 if (slen > FUZZY_SCORER_MAX_LENGTH) {
965 return -MIN_SCORE;
966 }
967 glong pi, si;
968 // whether we are aligning the first character of pattern
969 gboolean pfirst = TRUE;
970 // whether the start of a word in pattern
971 gboolean pstart = TRUE;
972 // score for each position
973 int *score = g_malloc_n(slen, sizeof(int));
974 // dp[i]: maximum value by aligning pattern[0..pi] to str[0..si]
975 int *dp = g_malloc_n(slen, sizeof(int));
976 // uleft: value of the upper left cell; ulefts: maximum value of uleft and
977 // cells on the left. The arbitrary initial values suppress warnings.
978 int uleft = 0, ulefts = 0, left, lefts;
979 const gchar *pit = pattern, *sit;
980 enum CharClass prev = NON_WORD;
981 for (si = 0, sit = str; si < slen; si++, sit = g_utf8_next_char(sit)) {
982 enum CharClass cur = rofi_scorer_get_character_class(g_utf8_get_char(sit));
983 score[si] = rofi_scorer_get_score_for(prev, cur);
984 prev = cur;
985 dp[si] = MIN_SCORE;
986 }
987 for (pi = 0; pi < plen; pi++, pit = g_utf8_next_char(pit)) {
988 gunichar pc = g_utf8_get_char(pit), sc;
989 if (g_unichar_isspace(pc)) {
990 pstart = TRUE;
991 continue;
992 }
993 lefts = MIN_SCORE;
994 for (si = 0, sit = str; si < slen; si++, sit = g_utf8_next_char(sit)) {
995 left = dp[si];
996 lefts = MAX(lefts + GAP_SCORE, left);
997 sc = g_utf8_get_char(sit);
998 if (case_sensitive ? pc == sc
999 : g_unichar_tolower(pc) == g_unichar_tolower(sc)) {
1000 int t = score[si] * (pstart ? PATTERN_START_MULTIPLIER
1002 dp[si] = pfirst ? LEADING_GAP_SCORE * si + t
1003 : MAX(uleft + CONSECUTIVE_SCORE, ulefts + t);
1004 } else {
1005 dp[si] = MIN_SCORE;
1006 }
1007 uleft = left;
1008 ulefts = lefts;
1009 }
1010 pfirst = pstart = FALSE;
1011 }
1012 lefts = MIN_SCORE;
1013 for (si = 0; si < slen; si++) {
1014 lefts = MAX(lefts + GAP_SCORE, dp[si]);
1015 }
1016 g_free(score);
1017 g_free(dp);
1018 return -lefts;
1019}
1020
1032int utf8_strncmp(const char *a, const char *b, size_t n) {
1033 char *na = g_utf8_normalize(a, -1, G_NORMALIZE_ALL_COMPOSE);
1034 char *nb = g_utf8_normalize(b, -1, G_NORMALIZE_ALL_COMPOSE);
1035 *g_utf8_offset_to_pointer(na, n) = '\0';
1036 *g_utf8_offset_to_pointer(nb, n) = '\0';
1037 int r = g_utf8_collate(na, nb);
1038 g_free(na);
1039 g_free(nb);
1040 return r;
1041}
1042
1043gboolean helper_execute(const char *wd, char **args, const char *error_precmd,
1044 const char *error_cmd,
1045 RofiHelperExecuteContext *context) {
1046 gboolean retv = TRUE;
1047 GError *error = NULL;
1048
1049 GSpawnChildSetupFunc child_setup = NULL;
1050 gpointer user_data = NULL;
1051
1052 display_startup_notification(context, &child_setup, &user_data);
1053
1054 g_spawn_async(wd, args, NULL, G_SPAWN_SEARCH_PATH, child_setup, user_data,
1055 NULL, &error);
1056 if (error != NULL) {
1057 char *msg = g_strdup_printf("Failed to execute: '%s%s'\nError: '%s'",
1058 error_precmd, error_cmd, error->message);
1059 rofi_view_error_dialog(msg, FALSE);
1060 g_free(msg);
1061 // print error.
1062 g_error_free(error);
1063 retv = FALSE;
1064 }
1065
1066 // Free the args list.
1067 g_strfreev(args);
1068 return retv;
1069}
1070
1071gboolean helper_execute_command(const char *wd, const char *cmd,
1072 gboolean run_in_term,
1073 RofiHelperExecuteContext *context) {
1074 char **args = NULL;
1075 int argc = 0;
1076
1077 if (run_in_term) {
1078 helper_parse_setup(config.run_shell_command, &args, &argc, "{cmd}", cmd,
1079 (char *)0);
1080 } else {
1081 helper_parse_setup(config.run_command, &args, &argc, "{cmd}", cmd,
1082 (char *)0);
1083 }
1084
1085 if (args == NULL) {
1086 return FALSE;
1087 }
1088
1089 if (context != NULL) {
1090 if (context->name == NULL) {
1091 context->name = args[0];
1092 }
1093 if (context->binary == NULL) {
1094 context->binary = args[0];
1095 }
1096 if (context->description == NULL) {
1097 gsize l = strlen("Launching '' via rofi") + strlen(cmd) + 1;
1098 gchar *description = g_newa(gchar, l);
1099
1100 g_snprintf(description, l, "Launching '%s' via rofi", cmd);
1101 context->description = description;
1102 }
1103 if (context->command == NULL) {
1104 context->command = cmd;
1105 }
1106 }
1107
1108 return helper_execute(wd, args, "", cmd, context);
1109}
1110
1111static char *helper_get_theme_path_check_file(const char *filename,
1112 const char *parent_file) {
1113
1114 // Check if absolute path.
1115 if (g_path_is_absolute(filename)) {
1116 g_debug("Opening theme, path is absolute: %s", filename);
1117 if (g_file_test(filename, G_FILE_TEST_EXISTS)) {
1118 return g_strdup(filename);
1119 }
1120 g_debug("Opening theme, path is absolute but does not exists: %s",
1121 filename);
1122 } else {
1123 if (parent_file != NULL) {
1124 // If no absolute path specified, expand it.
1125 char *basedir = g_path_get_dirname(parent_file);
1126 char *path = g_build_filename(basedir, filename, NULL);
1127 g_free(basedir);
1128 g_debug("Opening theme, check in dir where file is included: %s", path);
1129 if (g_file_test(path, G_FILE_TEST_EXISTS)) {
1130 return path;
1131 }
1132 g_debug("Opening theme, file does not exists in dir where file is "
1133 "included: %s\n",
1134 filename);
1135 }
1136 }
1137 // Check config's themes directory.
1138 const char *cpath = g_get_user_config_dir();
1139 if (cpath) {
1140 char *themep = g_build_filename(cpath, "rofi", "themes", filename, NULL);
1141 g_debug("Opening theme, testing: %s", themep);
1142 if (themep && g_file_test(themep, G_FILE_TEST_EXISTS)) {
1143 return themep;
1144 }
1145 g_free(themep);
1146 }
1147 // Check config directory.
1148 if (cpath) {
1149 char *themep = g_build_filename(cpath, "rofi", filename, NULL);
1150 g_debug("Opening theme, testing: %s", themep);
1151 if (g_file_test(themep, G_FILE_TEST_EXISTS)) {
1152 return themep;
1153 }
1154 g_free(themep);
1155 }
1156 const char *datadir = g_get_user_data_dir();
1157 if (datadir) {
1158 char *theme_path =
1159 g_build_filename(datadir, "rofi", "themes", filename, NULL);
1160 if (theme_path) {
1161 g_debug("Opening theme, testing: %s", theme_path);
1162 if (g_file_test(theme_path, G_FILE_TEST_EXISTS)) {
1163 return theme_path;
1164 }
1165 g_free(theme_path);
1166 }
1167 }
1168
1169 const gchar *const *system_data_dirs = g_get_system_data_dirs();
1170 if (system_data_dirs) {
1171 for (uint_fast32_t i = 0; system_data_dirs[i] != NULL; i++) {
1172 const char *const sdatadir = system_data_dirs[i];
1173 g_debug("Opening theme directory: %s", sdatadir);
1174 char *theme_path =
1175 g_build_filename(sdatadir, "rofi", "themes", filename, NULL);
1176 if (theme_path) {
1177 g_debug("Opening theme, testing: %s", theme_path);
1178 if (g_file_test(theme_path, G_FILE_TEST_EXISTS)) {
1179 return theme_path;
1180 }
1181 g_free(theme_path);
1182 }
1183 }
1184 }
1185
1186 char *theme_path = g_build_filename(THEME_DIR, filename, NULL);
1187 if (theme_path) {
1188 g_debug("Opening theme, testing: %s", theme_path);
1189 if (g_file_test(theme_path, G_FILE_TEST_EXISTS)) {
1190 return theme_path;
1191 }
1192 g_free(theme_path);
1193 }
1194 return NULL;
1195}
1196
1197char *helper_get_theme_path(const char *file, const char **ext,
1198 const char *parent_file) {
1199
1200 char *filename = rofi_expand_path(file);
1201 g_debug("Opening theme, testing: %s\n", filename);
1202 if (g_path_is_absolute(filename)) {
1203 g_debug("Opening theme, path is absolute: %s", filename);
1204 if (g_file_test(filename, G_FILE_TEST_EXISTS)) {
1205 return filename;
1206 }
1207 }
1208 gboolean ext_found = FALSE;
1209 for (const char **i = ext; *i != NULL; i++) {
1210 if (g_str_has_suffix(file, *i)) {
1211 ext_found = TRUE;
1212 break;
1213 }
1214 }
1215 if (ext_found) {
1216 filename = rofi_expand_path(file);
1217
1218 char *retv = helper_get_theme_path_check_file(filename, parent_file);
1219 if (retv) {
1220 g_free(filename);
1221 return retv;
1222 }
1223 } else {
1224 g_assert_nonnull(ext[0]);
1225 // Iterate through extensions.
1226 char *temp = filename;
1227 for (const char **i = ext; *i != NULL; i++) {
1228 filename = g_strconcat(temp, *i, NULL);
1229 char *retv = helper_get_theme_path_check_file(filename, parent_file);
1230 if (retv) {
1231 g_free(filename);
1232 g_free(temp);
1233 return retv;
1234 }
1235 }
1236 g_free(temp);
1237 }
1238
1239 return filename;
1240}
1241
1242static gboolean parse_pair(char *input, rofi_range_pair *item) {
1243 // Skip leading blanks.
1244 while (input != NULL && isblank(*input)) {
1245 ++input;
1246 }
1247
1248 if (input == NULL) {
1249 return FALSE;
1250 }
1251
1252 const char *sep[] = {"-", ":"};
1253 int pythonic = (strchr(input, ':') || input[0] == '-') ? 1 : 0;
1254 int index = 0;
1255
1256 for (char *token = strsep(&input, sep[pythonic]); token != NULL;
1257 token = strsep(&input, sep[pythonic])) {
1258 if (index == 0) {
1259 item->start = item->stop = (int)strtol(token, NULL, 10);
1260 index++;
1261 continue;
1262 }
1263
1264 if (token[0] == '\0') {
1265 item->stop = -1;
1266 continue;
1267 }
1268
1269 item->stop = (int)strtol(token, NULL, 10);
1270 if (pythonic) {
1271 --item->stop;
1272 }
1273 }
1274 return TRUE;
1275}
1276void parse_ranges(char *input, rofi_range_pair **list, unsigned int *length) {
1277 char *endp;
1278 if (input == NULL) {
1279 return;
1280 }
1281 const char *const sep = ",";
1282 for (char *token = strtok_r(input, sep, &endp); token != NULL;
1283 token = strtok_r(NULL, sep, &endp)) {
1284 // Make space.
1285 *list =
1286 g_realloc((*list), ((*length) + 1) * sizeof(struct rofi_range_pair));
1287 // Parse a single pair.
1288 if (parse_pair(token, &((*list)[*length]))) {
1289 (*length)++;
1290 }
1291 }
1292}
1293
1294int parse_case_sensitivity(const char *input) {
1295 int case_sensitive = config.case_sensitive;
1296 if (config.case_smart) {
1297 // By default case is false, unless the search query has a
1298 // uppercase in it?
1299 case_sensitive = FALSE;
1300 const char *end;
1301 if (g_utf8_validate(input, -1, &end)) {
1302 for (const char *c = (input); !case_sensitive && c != NULL && *c;
1303 c = g_utf8_next_char(c)) {
1304 gunichar uc = g_utf8_get_char(c);
1305 if (g_unichar_isupper(uc)) {
1306 case_sensitive = TRUE;
1307 }
1308 }
1309 }
1310 }
1311
1312 return case_sensitive;
1313}
1314
1315void rofi_output_formatted_line(const char *format, const char *string,
1316 int selected_line, const char *filter) {
1317 for (int i = 0; format && format[i]; i++) {
1318 if (format[i] == 'i') {
1319 fprintf(stdout, "%d", selected_line);
1320 } else if (format[i] == 'd') {
1321 fprintf(stdout, "%d", (selected_line + 1));
1322 } else if (format[i] == 's') {
1323 fputs(string, stdout);
1324 } else if (format[i] == 'p') {
1325 char *esc = NULL;
1326 pango_parse_markup(string, -1, 0, NULL, &esc, NULL, NULL);
1327 if (esc) {
1328 fputs(esc, stdout);
1329 g_free(esc);
1330 } else {
1331 fputs("invalid string", stdout);
1332 }
1333 } else if (format[i] == 'q') {
1334 char *quote = g_shell_quote(string);
1335 fputs(quote, stdout);
1336 g_free(quote);
1337 } else if (format[i] == 'f') {
1338 if (filter) {
1339 fputs(filter, stdout);
1340 }
1341 } else if (format[i] == 'F') {
1342 if (filter) {
1343 char *quote = g_shell_quote(filter);
1344 fputs(quote, stdout);
1345 g_free(quote);
1346 }
1347 } else {
1348 fputc(format[i], stdout);
1349 }
1350 }
1351 fputc('\n', stdout);
1352 fflush(stdout);
1353}
1354
1355static gboolean helper_eval_cb2(const GMatchInfo *info, GString *res,
1356 gpointer data) {
1357 gchar *match;
1358 // Get the match
1359 int num_match = g_match_info_get_match_count(info);
1360 // Just {text} This is inside () 5.
1361 if (num_match == 5) {
1362 match = g_match_info_fetch(info, 4);
1363 if (match != NULL) {
1364 // Lookup the match, so we can replace it.
1365 gchar *r = g_hash_table_lookup((GHashTable *)data, match);
1366 if (r != NULL) {
1367 // Append the replacement to the string.
1368 g_string_append(res, r);
1369 }
1370 // Free match.
1371 g_free(match);
1372 }
1373 }
1374 // {} with [] guard around it.
1375 else if (num_match == 4) {
1376 match = g_match_info_fetch(info, 2);
1377 if (match != NULL) {
1378 // Lookup the match, so we can replace it.
1379 gchar *r = g_hash_table_lookup((GHashTable *)data, match);
1380 if (r != NULL) {
1381 // Add (optional) prefix
1382 gchar *prefix = g_match_info_fetch(info, 1);
1383 g_string_append(res, prefix);
1384 g_free(prefix);
1385 // Append the replacement to the string.
1386 g_string_append(res, r);
1387 // Add (optional) postfix
1388 gchar *post = g_match_info_fetch(info, 3);
1389 g_string_append(res, post);
1390 g_free(post);
1391 }
1392 // Free match.
1393 g_free(match);
1394 }
1395 }
1396 // Else we have an invalid match.
1397 // Continue replacement.
1398 return FALSE;
1399}
1400
1401char *helper_string_replace_if_exists(char *string, ...) {
1402 GHashTable *h;
1403 h = g_hash_table_new(g_str_hash, g_str_equal);
1404 va_list ap;
1405 va_start(ap, string);
1406 // Add list from variable arguments.
1407 while (1) {
1408 char *key = va_arg(ap, char *);
1409 if (key == (char *)0) {
1410 break;
1411 }
1412 char *value = va_arg(ap, char *);
1413 g_hash_table_insert(h, key, value);
1414 }
1415 char *retv = helper_string_replace_if_exists_v(string, h);
1416 va_end(ap);
1417 // Destroy key-value storage.
1418 g_hash_table_destroy(h);
1419 return retv;
1420}
1421
1436char *helper_string_replace_if_exists_v(char *string, GHashTable *h) {
1437 GError *error = NULL;
1438 char *res = NULL;
1439
1440 // Replace hits within {-\w+}.
1441 GRegex *reg = g_regex_new("\\[(.*)({[-\\w]+})(.*)\\]|({[\\w-]+})",
1442 G_REGEX_UNGREEDY, 0, &error);
1443 if (error == NULL) {
1444 res =
1445 g_regex_replace_eval(reg, string, -1, 0, 0, helper_eval_cb2, h, &error);
1446 }
1447 // Free regex.
1448 g_regex_unref(reg);
1449 // Throw error if shell parsing fails.
1450 if (error != NULL) {
1451 char *msg = g_strdup_printf("Failed to parse: '%s'\nError: '%s'", string,
1452 error->message);
1453 rofi_view_error_dialog(msg, FALSE);
1454 g_free(msg);
1455 // print error.
1456 g_error_free(error);
1457 g_free(res);
1458 return NULL;
1459 }
1460 return res;
1461}
@ WL_CENTER
Definition rofi-types.h:235
@ MM_NORMAL
Definition settings.h:39
@ MM_REGEX
Definition settings.h:40
@ MM_PREFIX
Definition settings.h:43
@ MM_FUZZY
Definition settings.h:42
@ MM_NUM_MATCHERS
Definition settings.h:44
@ MM_GLOB
Definition settings.h:41
void helper_token_match_set_pango_attr_on_style(PangoAttrList *retv, int start, int end, RofiHighlightColorStyle th)
Definition helper.c:442
PangoAttrList * helper_token_match_get_pango_attr(RofiHighlightColorStyle th, rofi_int_matcher **tokens, const char *input, PangoAttrList *retv)
Definition helper.c:511
gboolean helper_validate_font(PangoFontDescription *pfd, const char *font)
Definition helper.c:653
char * rofi_latin_to_utf8_strdup(const char *input, gssize length)
Definition helper.c:850
void parse_ranges(char *input, rofi_range_pair **list, unsigned int *length)
Definition helper.c:1276
void cmd_set_arguments(int argc, char **argv)
Definition helper.c:98
rofi_int_matcher ** helper_tokenize(const char *input, int case_sensitive)
Definition helper.c:287
unsigned int levenshtein(const char *needle, const glong needlelen, const char *haystack, const glong haystacklen, int case_sensitive)
Definition helper.c:813
int find_arg_char(const char *const key, char *val)
Definition helper.c:432
gboolean helper_execute_command(const char *wd, const char *cmd, gboolean run_in_term, RofiHelperExecuteContext *context)
Definition helper.c:1071
void helper_select_next_matching_mode(void)
Definition helper.c:84
void helper_tokenize_free(rofi_int_matcher **tokens)
Definition helper.c:147
char helper_parse_char(const char *arg)
Definition helper.c:384
void rofi_output_formatted_line(const char *format, const char *string, int selected_line, const char *filter)
Definition helper.c:1315
const char * helper_get_matching_mode_str(void)
Definition helper.c:81
gboolean helper_execute(const char *wd, char **args, const char *error_precmd, const char *error_cmd, RofiHelperExecuteContext *context)
Definition helper.c:1043
char * helper_string_replace_if_exists(char *string,...)
Definition helper.c:1401
const char ** find_arg_strv(const char *const key)
Definition helper.c:345
int helper_parse_setup(char *string, char ***output, int *length,...)
Definition helper.c:103
int execute_generator(const char *cmd)
Definition helper.c:563
void helper_select_previous_matching_mode(void)
Definition helper.c:90
int find_arg_int(const char *const key, int *val)
Definition helper.c:365
char * rofi_expand_path(const char *input)
Definition helper.c:781
void remove_pid_file(int fd)
Definition helper.c:645
int find_arg_str(const char *const key, char **val)
Definition helper.c:335
int parse_case_sensitivity(const char *input)
Definition helper.c:1294
int rofi_scorer_fuzzy_evaluate(const char *pattern, glong plen, const char *str, glong slen, int case_sensitive)
Definition helper.c:962
int find_arg_uint(const char *const key, unsigned int *val)
Definition helper.c:374
int find_arg(const char *const key)
Definition helper.c:326
int helper_token_match(rofi_int_matcher *const *tokens, const char *input)
Definition helper.c:542
int create_pid_file(const char *pidfile, gboolean kill_running)
Definition helper.c:586
int config_sanity_check(void)
Definition helper.c:672
char * rofi_force_utf8(const gchar *data, ssize_t length)
Definition helper.c:856
void rofi_add_error_message(GString *str)
Definition rofi.c:91
int rofi_view_error_dialog(const char *msg, int markup)
Definition view.c:2675
#define CONSECUTIVE_SCORE
Definition helper.c:903
#define GAP_SCORE
Definition helper.c:895
#define LEADING_GAP_SCORE
Definition helper.c:893
static gchar * prefix_regex(const char *input)
Definition helper.c:199
static int MatchingMethodEnabled[MM_NUM_MATCHERS]
Definition helper.c:61
char ** stored_argv
Definition helper.c:77
static char * utf8_helper_simplify_string(const char *os)
Definition helper.c:206
#define MIN3(a, b, c)
Definition helper.c:810
#define CAMEL_SCORE
Definition helper.c:901
static gchar * glob_to_regex(const char *input)
Definition helper.c:155
const char *const monitor_position_entries[]
Definition helper.c:71
static enum CharClass rofi_scorer_get_character_class(gunichar c)
Definition helper.c:928
int utf8_strncmp(const char *a, const char *b, size_t n)
Definition helper.c:1032
#define MIN_SCORE
Definition helper.c:891
#define PATTERN_NON_START_MULTIPLIER
Definition helper.c:905
char * helper_string_replace_if_exists_v(char *string, GHashTable *h)
Definition helper.c:1436
static int NUMMatchingMethodEnabled
Definition helper.c:65
#define WORD_START_SCORE
Definition helper.c:897
static char * helper_get_theme_path_check_file(const char *filename, const char *parent_file)
Definition helper.c:1111
static int CurrentMatchingMethod
Definition helper.c:66
#define FUZZY_SCORER_MAX_LENGTH
Definition helper.c:889
static gboolean helper_eval_cb2(const GMatchInfo *info, GString *res, gpointer data)
Definition helper.c:1355
#define PATTERN_START_MULTIPLIER
Definition helper.c:907
char * helper_get_theme_path(const char *file, const char **ext, const char *parent_file)
Definition helper.c:1197
static rofi_int_matcher * create_regex(const char *input, int case_sensitive)
Definition helper.c:246
static GRegex * R(const char *s, int case_sensitive)
Definition helper.c:231
#define NON_WORD_SCORE
Definition helper.c:899
static gchar * fuzzy_to_regex(const char *input)
Definition helper.c:170
const char *const MatchingMethodStr[MM_NUM_MATCHERS]
Definition helper.c:58
CharClass
Definition helper.c:912
@ DIGIT
Definition helper.c:918
@ LOWER
Definition helper.c:914
@ NON_WORD
Definition helper.c:920
@ UPPER
Definition helper.c:916
int stored_argc
Definition helper.c:75
static gboolean parse_pair(char *input, rofi_range_pair *item)
Definition helper.c:1242
static int rofi_scorer_get_score_for(enum CharClass prev, enum CharClass curr)
Definition helper.c:949
struct rofi_int_matcher_t rofi_int_matcher
@ ROFI_HL_UPPERCASE
Definition rofi-types.h:64
@ ROFI_HL_STRIKETHROUGH
Definition rofi-types.h:58
@ ROFI_HL_ITALIC
Definition rofi-types.h:60
@ ROFI_HL_UNDERLINE
Definition rofi-types.h:56
@ ROFI_HL_CAPITALIZE
Definition rofi-types.h:68
@ ROFI_HL_BOLD
Definition rofi-types.h:54
@ ROFI_HL_LOWERCASE
Definition rofi-types.h:66
@ ROFI_HL_COLOR
Definition rofi-types.h:62
char * pidfile
Definition rofi.c:78
Settings config
@ SORT_FZF
Definition settings.h:50
@ SORT_NORMAL
Definition settings.h:50
const gchar * binary
Definition helper.h:293
const gchar * description
Definition helper.h:295
const gchar * name
Definition helper.h:291
const gchar * command
Definition helper.h:303
RofiHighlightStyle style
Definition rofi-types.h:219
double blue
Definition rofi-types.h:162
double green
Definition rofi-types.h:160
double red
Definition rofi-types.h:158
double alpha
Definition rofi-types.h:164
workarea mon
Definition view.c:131
MenuFlags flags
Definition view.c:127
unsigned long long count
Definition view.c:149
int monitor_active(workarea *mon)
Definition xcb.c:1003
void display_startup_notification(RofiHelperExecuteContext *context, GSpawnChildSetupFunc *child_setup, gpointer *user_data)
Definition xcb.c:720
struct _workarea workarea