girara
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
utils.c
Go to the documentation of this file.
1 /* See LICENSE file for license and copyright information */
2 
3 #define _DEFAULT_SOURCE
4 #if !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__NetBSD__)
5 #define _XOPEN_SOURCE 700
6 #endif
7 #define _FILE_OFFSET_BITS 64
8 
9 #include <ctype.h>
10 #include <glib.h>
11 #include <glib/gi18n-lib.h>
12 #include <limits.h>
13 #include <pwd.h>
14 #include <stdbool.h>
15 #include <stdint.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/types.h>
20 #include <unistd.h>
21 
22 #include "utils.h"
23 #include "datastructures.h"
24 #include "session.h"
25 #include "settings.h"
26 
27 #define BLOCK_SIZE 64
28 
29 char*
30 girara_fix_path(const char* path)
31 {
32  if (path == NULL) {
33  return NULL;
34  }
35 
36  char* rpath = NULL;
37  if (path[0] == '~') {
38  const size_t len = strlen(path);
39  char* user = NULL;
40  size_t idx = 1;
41 
42  if (len > 1 && path[1] != '/') {
43  while (path[idx] && path[idx] != '/') {
44  ++idx;
45  }
46 
47  user = g_strndup(path + 1, idx - 1);
48  }
49 
50  char* home_path = girara_get_home_directory(user);
51  g_free(user);
52 
53  if (home_path == NULL) {
54  return g_strdup(path);
55  }
56 
57  rpath = g_build_filename(home_path, path + idx, NULL);
58  g_free(home_path);
59  } else {
60  rpath = g_strdup(path);
61  }
62 
63  return rpath;
64 }
65 
66 bool
67 girara_xdg_open(const char* uri)
68 {
69  if (uri == NULL || strlen(uri) == 0) {
70  return false;
71  }
72 
73  GString* command = g_string_new("xdg-open ");
74  char* tmp = g_shell_quote(uri);
75 
76  g_string_append(command, tmp);
77  g_free(tmp);
78 
79  GError* error = NULL;
80  bool res = g_spawn_command_line_async(command->str, &error);
81  if (error != NULL) {
82  girara_warning("Failed to execute command: %s", error->message);
83  g_error_free(error);
84  }
85 
86  g_string_free(command, TRUE);
87  return res;
88 }
89 
90 char*
91 girara_get_home_directory(const char* user)
92 {
93  if (user == NULL || g_strcmp0(user, g_get_user_name()) == 0) {
94 #if GLIB_CHECK_VERSION(2, 35, 3)
95  return g_strdup(g_get_home_dir());
96 #else
97  const char* homedir = g_getenv("HOME");
98  return g_strdup(homedir ? homedir : g_get_home_dir());
99 #endif
100  }
101 
102  // XXX: The following code is very unportable.
103  struct passwd pwd;
104  struct passwd* result = NULL;
105 #ifdef _SC_GETPW_R_SIZE_MAX
106  int bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
107  if (bufsize < 0) {
108  bufsize = 4096;
109  }
110 #else
111  int bufsize = 4096;
112 #endif
113 
114  char* buffer = g_try_malloc0(sizeof(char) * bufsize);
115  if (buffer == NULL) {
116  return NULL;
117  }
118 
119  getpwnam_r(user, &pwd, buffer, bufsize, &result);
120  if (result == NULL) {
121  g_free(buffer);
122  return NULL;
123  }
124 
125  char* dir = g_strdup(pwd.pw_dir);
126  g_free(buffer);
127  return dir;
128 }
129 
130 char*
132 {
133  static const char* VARS[] = {
134  "XDG_CONFIG_HOME",
135  "XDG_DATA_HOME",
136  "XDG_CONFIG_DIRS",
137  "XDG_DATA_DIRS",
138  "XDG_CACHE_HOME",
139  };
140 
141  static const char* DEFAULTS[] = {
142  "NOTUSED",
143  "NOTUSED",
144  "/etc/xdg",
145  "/usr/local/share/:/usr/share",
146  "NOTUSED"
147  };
148 
149  switch (path) {
150  case XDG_DATA:
151  return g_strdup(g_get_user_data_dir());
152  case XDG_CONFIG:
153  return g_strdup(g_get_user_config_dir());
154  case XDG_CONFIG_DIRS:
155  case XDG_DATA_DIRS:
156  {
157  const char* tmp = g_getenv(VARS[path]);
158  if (tmp == NULL || !g_strcmp0(tmp, "")) {
159  return g_strdup(DEFAULTS[path]);
160  }
161  return g_strdup(tmp);
162  }
163  case XDG_CACHE:
164  return g_strdup(g_get_user_cache_dir());
165  }
166 
167  return NULL;
168 }
169 
170 girara_list_t*
171 girara_split_path_array(const char* patharray)
172 {
173  if (patharray == NULL || !g_strcmp0(patharray, "")) {
174  return NULL;
175  }
176 
177  girara_list_t* res = girara_list_new2(g_free);
178  char** paths = g_strsplit(patharray, ":", 0);
179  for (size_t i = 0; paths[i] != NULL; ++i) {
180  girara_list_append(res, g_strdup(paths[i]));
181  }
182  g_strfreev(paths);
183 
184  return res;
185 }
186 
187 FILE*
188 girara_file_open(const char* path, const char* mode)
189 {
190  char* fixed_path = girara_fix_path(path);
191 
192  if (fixed_path == NULL || mode == NULL) {
193  return NULL;
194  }
195 
196  FILE* fp = fopen(fixed_path, mode);
197  g_free(fixed_path);
198  if (fp == NULL) {
199  return NULL;
200  }
201 
202  return fp;
203 
204  /* TODO */
205  /*FILE* fp;*/
206  /*struct stat lstat;*/
207  /*struct stat fstat;*/
208  /*int fd;*/
209  /*char* mode = "rb+";*/
210 
211  /*if (lstat(path, &lstat) == -1) {*/
212  /*if (errno != ENOENT) {*/
213  /*return NULL;*/
214  /*}*/
215 
216  /*if ((fd = open(path, O_CREAT | O_EXCL | O_RDWR, 0600)) == -1) {*/
217  /*return NULL;*/
218  /*}*/
219 
220  /*mode = "wb";*/
221  /*} else {*/
222  /*if ((fd = open(path, O_RDONLY)) == -1) {*/
223  /*return NULL;*/
224  /*}*/
225 
226  /*if (fstat(fd, &fstat) == -1) {*/
227  /*if (lstat.st_mode != fstat.st_mode ||*/
228  /*lstat.st_ino != fstat.st_ino ||*/
229  /*lstat.st_dev != fstat.st_dev) {*/
230  /*close(fd);*/
231  /*return NULL;*/
232  /*}*/
233  /*}*/
234 
235  /*ftruncate(fd, 0);*/
236  /*}*/
237 
238  /*if ((fp = fdopen(fd, mode)) == NULL) {*/
239  /*close(fd);*/
240  /*unlink(path);*/
241  /*return NULL;*/
242  /*}*/
243 
244  /*return fp;*/
245 }
246 
247 #if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__)
248 char*
249 girara_file_read_line(FILE* file)
250 {
251  if (file == NULL) {
252  return NULL;
253  }
254 
255  size_t size = 0;
256  char* line = fgetln(file, &size);
257  if (line == NULL) {
258  return NULL;
259  }
260 
261  char* copy = strndup(line, size);
262  if (copy == NULL) {
263  return NULL;
264  }
265 
266  /* remove the trailing line deliminator */
267  g_strdelimit(copy, "\n\r", '\0');
268 
269  return copy;
270 }
271 #else
272 char*
274 {
275  if (file == NULL) {
276  return NULL;
277  }
278 
279  size_t size = 0;
280  char* line = NULL;
281  if (getline(&line, &size, file) == -1) {
282  if (line != NULL) {
283  free(line);
284  }
285  return NULL;
286  }
287 
288  /* remove the trailing line deliminator */
289  g_strdelimit(line, "\n\r", '\0');
290  return line;
291 }
292 #endif
293 
294 char*
295 girara_file_read(const char* path)
296 {
297  if (path == NULL) {
298  return NULL;
299  }
300 
301  FILE* file = girara_file_open(path, "r");
302  if (file == NULL) {
303  return NULL;
304  }
305 
306  char* content = girara_file_read2(file);
307  fclose(file);
308  return content;
309 }
310 
311 char*
312 girara_file_read2(FILE* file)
313 {
314  if (file == NULL) {
315  return NULL;
316  }
317 
318  const off_t curpos = ftello(file);
319  if (curpos == -1) {
320  return NULL;
321  }
322 
323  fseeko(file, 0, SEEK_END);
324  const off_t size = ftello(file) - curpos;
325  fseeko(file, curpos, SEEK_SET);
326 
327  if (size == 0) {
328  char* content = malloc(1);
329  content[0] = '\0';
330  return content;
331  }
332  /* this can happen on 32 bit systems */
333  if ((uintmax_t)size >= (uintmax_t)SIZE_MAX) {
334  girara_error("file is too large");
335  return NULL;
336  }
337 
338  char* buffer = malloc(size + 1);
339  if (buffer == NULL) {
340  return NULL;
341  }
342 
343  size_t read = fread(buffer, size, 1, file);
344  if (read != 1) {
345  free(buffer);
346  return NULL;
347  }
348 
349  buffer[size] = '\0';
350  return buffer;
351 }
352 
353 void
354 girara_clean_line(char* line)
355 {
356  if (line == NULL) {
357  return;
358  }
359 
360  unsigned int i = 0;
361  unsigned int j = 0;
362  bool ws_mode = true;
363 
364  for(i = 0; i < strlen(line); i++) {
365  if (isspace(line[i]) != 0) {
366  if (ws_mode == true) {
367  continue;
368  }
369 
370  line[j++] = ' ';
371  ws_mode = true;
372  } else {
373  line[j++] = line[i];
374  ws_mode = false;
375  }
376  }
377 
378  line[j] = '\0';
379 }
380 
381 void*
382 girara_safe_realloc(void** ptr, size_t size)
383 {
384  if (ptr == NULL) {
385  return NULL;
386  }
387 
388  if (size == 0) {
389  goto error_free;
390  }
391 
392  void* tmp = realloc(*ptr, size);
393  if (tmp == NULL) {
394  goto error_free;
395  }
396 
397  *ptr = tmp;
398  return *ptr;
399 
400 error_free:
401 
402  free(*ptr);
403  *ptr = NULL;
404 
405  return NULL;
406 }
407 
408 void
409 update_state_by_keyval(int *state, int keyval)
410 {
411  if (state == NULL) {
412  return;
413  }
414 
415  if ((keyval >= '!' && keyval <= '/')
416  || (keyval >= ':' && keyval <= '@')
417  || (keyval >= '[' && keyval <= '`')
418  || (keyval >= '{' && keyval <= '~')
419  ) {
420  *state |= GDK_SHIFT_MASK;
421  }
422 }
423 
424 char*
425 girara_escape_string(const char* value)
426 {
427  if (value == NULL) {
428  return NULL;
429  }
430 
431  GString* str = g_string_new("");
432  while (*value != '\0') {
433  const char c = *value++;
434  if (strchr("\\ \t\"\'", c) != NULL) {
435  g_string_append_c(str, '\\');
436  }
437  g_string_append_c(str, c);
438  }
439 
440  return g_string_free(str, FALSE);
441 }
442 
443 char*
444 girara_replace_substring(const char* string, const char* old, const char* new)
445 {
446  if (string == NULL || old == NULL || new == NULL) {
447  return NULL;
448  }
449 
450  size_t old_len = strlen(old);
451  size_t new_len = strlen(new);
452 
453  /* count occurrences */
454  size_t count = 0;
455  size_t i = 0;
456 
457  for (i = 0; string[i] != '\0'; i++) {
458  if (strstr(&string[i], old) == &string[i]) {
459  i += (old_len - 1);
460  count++;
461  }
462  }
463 
464  if (count == 0) {
465  return g_strdup(string);
466  }
467 
468  char* ret = g_try_malloc0(sizeof(char) * (i - count * old_len + count * new_len + 1));
469  if (ret == NULL) {
470  return NULL;
471  }
472 
473  /* replace */
474  char* iter = ret;
475  while (*string != '\0') {
476  if (strstr(string, old) == string) {
477  strncpy(iter, new, new_len);
478  iter += new_len;
479  string += old_len;
480  } else {
481  *iter++ = *string++;
482  }
483  }
484 
485  return ret;
486 }
487 
488 bool
489 girara_exec_with_argument_list(girara_session_t* session, girara_list_t* argument_list)
490 {
491  if (session == NULL || argument_list == NULL) {
492  return false;
493  }
494 
495  char* cmd = NULL;
496  girara_setting_get(session, "exec-command", &cmd);
497  if (cmd == NULL || strlen(cmd) == 0) {
498  girara_debug("exec-command is empty, executing directly.");
499  g_free(cmd);
500  cmd = NULL;
501  }
502 
503  bool dont_append_first_space = cmd == NULL;
504  GString* command = g_string_new(cmd ? cmd : "");
505  g_free(cmd);
506 
507  GIRARA_LIST_FOREACH(argument_list, char*, iter, value)
508  if (dont_append_first_space == false) {
509  g_string_append_c(command, ' ');
510  }
511  dont_append_first_space = false;
512  char* tmp = g_shell_quote(value);
513  g_string_append(command, tmp);
514  g_free(tmp);
515  GIRARA_LIST_FOREACH_END(argument_list, char*, iter, value);
516 
517  GError* error = NULL;
518  girara_info("executing: %s", command->str);
519  gboolean ret = g_spawn_command_line_async(command->str, &error);
520  if (error != NULL) {
521  girara_warning("Failed to execute command: %s", error->message);
522  girara_notify(session, GIRARA_ERROR, _("Failed to execute command: %s"), error->message);
523  g_error_free(error);
524  }
525 
526  g_string_free(command, TRUE);
527 
528  return ret;
529 }
530 
531 void
532 widget_add_class(GtkWidget* widget, const char* styleclass)
533 {
534  if (widget == NULL || styleclass == NULL) {
535  return;
536  }
537 
538  GtkStyleContext* context = gtk_widget_get_style_context(widget);
539  gtk_style_context_add_class(context, styleclass);
540 }
541 
char * girara_replace_substring(const char *string, const char *old, const char *new)
Definition: utils.c:444
#define girara_debug(...)
Definition: utils.h:119
void girara_list_append(girara_list_t *list, void *data)
FILE * girara_file_open(const char *path, const char *mode)
Definition: utils.c:188
girara_list_t * girara_split_path_array(const char *patharray)
Definition: utils.c:171
girara_list_t * girara_list_new2(girara_free_function_t gfree)
girara_xdg_path_t
Definition: utils.h:13
bool girara_xdg_open(const char *uri)
Definition: utils.c:67
#define girara_info(...)
Definition: utils.h:124
void update_state_by_keyval(int *state, int keyval)
Definition: utils.c:409
void girara_clean_line(char *line)
Definition: utils.c:354
char * girara_file_read(const char *path)
Definition: utils.c:295
Definition: utils.h:15
char * girara_file_read_line(FILE *file)
Definition: utils.c:273
char * girara_fix_path(const char *path)
Definition: utils.c:30
bool girara_setting_get(girara_session_t *session, const char *name, void *dest)
Definition: settings.c:140
#define girara_error(...)
Definition: utils.h:134
#define girara_warning(...)
Definition: utils.h:129
char * girara_file_read2(FILE *file)
Definition: utils.c:312
void * girara_safe_realloc(void **ptr, size_t size)
Definition: utils.c:382
void girara_notify(girara_session_t *session, int level, const char *format,...)
Definition: session.c:692
char * girara_get_xdg_path(girara_xdg_path_t path)
Definition: utils.c:131
bool girara_exec_with_argument_list(girara_session_t *session, girara_list_t *argument_list)
Definition: utils.c:489
char * girara_escape_string(const char *value)
Definition: utils.c:425
void widget_add_class(GtkWidget *widget, const char *styleclass)
Definition: utils.c:532
#define GIRARA_LIST_FOREACH_END(list, type, iter, data)
#define GIRARA_LIST_FOREACH(list, type, iter, data)
char * girara_get_home_directory(const char *user)
Definition: utils.c:91