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