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