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