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