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