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