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