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  const char* homedir = g_getenv("HOME");
98  return g_strdup(homedir ? homedir : g_get_home_dir());
99  }
100 
101  // XXX: The following code is very unportable.
102  struct passwd pwd;
103  struct passwd* result;
104 #ifdef _SC_GETPW_R_SIZE_MAX
105  int bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
106  if (bufsize < 0) {
107  bufsize = 4096;
108  }
109 #else
110  int bufsize = 4096;
111 #endif
112 
113  char* buffer = g_malloc0(sizeof(char) * bufsize);
114 
115  getpwnam_r(user, &pwd, buffer, bufsize, &result);
116  if (result == NULL) {
117  g_free(buffer);
118  return NULL;
119  }
120 
121  char* dir = g_strdup(pwd.pw_dir);
122  g_free(buffer);
123  return dir;
124 }
125 
126 char*
128 {
129  static const char* VARS[] = {
130  "XDG_CONFIG_HOME",
131  "XDG_DATA_HOME",
132  "XDG_CONFIG_DIRS",
133  "XDG_DATA_DIRS"
134  };
135 
136  static const char* DEFAULTS[] = {
137  "NOTUSED",
138  "NOTUSED",
139  "/etc/xdg",
140  "/usr/local/share/:/usr/share",
141  };
142 
143  switch (path) {
144  case XDG_DATA:
145  return g_strdup(g_get_user_data_dir());
146  case XDG_CONFIG:
147  return g_strdup(g_get_user_config_dir());
148  case XDG_CONFIG_DIRS:
149  case XDG_DATA_DIRS:
150  {
151  const char* tmp = g_getenv(VARS[path]);
152  if (tmp == NULL || !g_strcmp0(tmp, "")) {
153  return g_strdup(DEFAULTS[path]);
154  }
155  return g_strdup(tmp);
156  }
157  }
158 
159  return NULL;
160 }
161 
162 girara_list_t*
163 girara_split_path_array(const char* patharray)
164 {
165  if (patharray == NULL || !g_strcmp0(patharray, "")) {
166  return NULL;
167  }
168 
169  girara_list_t* res = girara_list_new2(g_free);
170  char** paths = g_strsplit(patharray, ":", 0);
171  for (size_t i = 0; paths[i] != NULL; ++i) {
172  girara_list_append(res, g_strdup(paths[i]));
173  }
174  g_strfreev(paths);
175 
176  return res;
177 }
178 
179 FILE*
180 girara_file_open(const char* path, const char* mode)
181 {
182  char* fixed_path = girara_fix_path(path);
183 
184  if (fixed_path == NULL || mode == NULL) {
185  return NULL;
186  }
187 
188  FILE* fp = fopen(fixed_path, mode);
189  g_free(fixed_path);
190  if (fp == NULL) {
191  return NULL;
192  }
193 
194  return fp;
195 
196  /* TODO */
197  /*FILE* fp;*/
198  /*struct stat lstat;*/
199  /*struct stat fstat;*/
200  /*int fd;*/
201  /*char* mode = "rb+";*/
202 
203  /*if (lstat(path, &lstat) == -1) {*/
204  /*if (errno != ENOENT) {*/
205  /*return NULL;*/
206  /*}*/
207 
208  /*if ((fd = open(path, O_CREAT | O_EXCL | O_RDWR, 0600)) == -1) {*/
209  /*return NULL;*/
210  /*}*/
211 
212  /*mode = "wb";*/
213  /*} else {*/
214  /*if ((fd = open(path, O_RDONLY)) == -1) {*/
215  /*return NULL;*/
216  /*}*/
217 
218  /*if (fstat(fd, &fstat) == -1) {*/
219  /*if (lstat.st_mode != fstat.st_mode ||*/
220  /*lstat.st_ino != fstat.st_ino ||*/
221  /*lstat.st_dev != fstat.st_dev) {*/
222  /*close(fd);*/
223  /*return NULL;*/
224  /*}*/
225  /*}*/
226 
227  /*ftruncate(fd, 0);*/
228  /*}*/
229 
230  /*if ((fp = fdopen(fd, mode)) == NULL) {*/
231  /*close(fd);*/
232  /*unlink(path);*/
233  /*return NULL;*/
234  /*}*/
235 
236  /*return fp;*/
237 }
238 
239 #if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__)
240 char*
241 girara_file_read_line(FILE* file)
242 {
243  if (file == NULL) {
244  return NULL;
245  }
246 
247  size_t size = 0;
248  char* line = fgetln(file, &size);
249  if (line == NULL) {
250  return NULL;
251  }
252 
253  char* copy = strndup(line, size);
254  if (copy == NULL) {
255  return NULL;
256  }
257 
258  /* remove the trailing line deliminator */
259  g_strdelimit(copy, "\n\r", '\0');
260 
261  return copy;
262 }
263 #else
264 char*
266 {
267  if (file == NULL) {
268  return NULL;
269  }
270 
271  size_t size = 0;
272  char* line = NULL;
273  if (getline(&line, &size, file) == -1) {
274  if (line != NULL) {
275  free(line);
276  }
277  return NULL;
278  }
279 
280  /* remove the trailing line deliminator */
281  g_strdelimit(line, "\n\r", '\0');
282  return line;
283 }
284 #endif
285 
286 char*
287 girara_file_read(const char* path)
288 {
289  if (path == NULL) {
290  return NULL;
291  }
292 
293  FILE* file = girara_file_open(path, "r");
294  if (file == NULL) {
295  return NULL;
296  }
297 
298  char* content = girara_file_read2(file);
299  fclose(file);
300  return content;
301 }
302 
303 char*
304 girara_file_read2(FILE* file)
305 {
306  if (file == NULL) {
307  return NULL;
308  }
309 
310  const off_t curpos = ftello(file);
311  if (curpos == -1) {
312  return NULL;
313  }
314 
315  fseeko(file, 0, SEEK_END);
316  const off_t size = ftello(file) - curpos;
317  fseeko(file, curpos, SEEK_SET);
318 
319  if (size == 0) {
320  char* content = malloc(1);
321  content[0] = '\0';
322  return content;
323  }
324  /* this can happen on 32 bit systems */
325  if ((uintmax_t)size >= (uintmax_t)SIZE_MAX) {
326  girara_error("file is too large");
327  return NULL;
328  }
329 
330  char* buffer = malloc(size + 1);
331  if (!buffer) {
332  return NULL;
333  }
334 
335  size_t read = fread(buffer, size, 1, file);
336  if (read != 1) {
337  free(buffer);
338  return NULL;
339  }
340 
341  buffer[size] = '\0';
342  return buffer;
343 }
344 
345 void
346 girara_clean_line(char* line)
347 {
348  if (line == NULL) {
349  return;
350  }
351 
352  unsigned int i = 0;
353  unsigned int j = 0;
354  bool ws_mode = true;
355 
356  for(i = 0; i < strlen(line); i++) {
357  if (isspace(line[i]) != 0) {
358  if (ws_mode) {
359  continue;
360  }
361 
362  line[j++] = ' ';
363  ws_mode = true;
364  } else {
365  line[j++] = line[i];
366  ws_mode = false;
367  }
368  }
369 
370  line[j] = '\0';
371 }
372 
373 void*
374 girara_safe_realloc(void** ptr, size_t size)
375 {
376  if(ptr == NULL) {
377  return NULL;
378  }
379 
380  if (size == 0) {
381  goto error_free;
382  }
383 
384  void* tmp = realloc(*ptr, size);
385  if(tmp == NULL) {
386  goto error_free;
387  }
388 
389  *ptr = tmp;
390  return *ptr;
391 
392 error_free:
393 
394  free(*ptr);
395  *ptr = NULL;
396 
397  return NULL;
398 }
399 
400 static girara_debug_level_t debug_level = GIRARA_DEBUG;
401 
402 void
403 _girara_debug(const char* function, int line, girara_debug_level_t level, const char* format, ...)
404 {
405  if (level < debug_level) {
406  return;
407  }
408 
409  switch (level)
410  {
411  case GIRARA_WARNING:
412  fprintf(stderr, "warning: ");
413  break;
414  case GIRARA_ERROR:
415  fprintf(stderr, "error: ");
416  break;
417  case GIRARA_INFO:
418  fprintf(stderr, "info: ");
419  break;
420  case GIRARA_DEBUG:
421  fprintf(stderr, "debug: (%s:%d) ", function, line);
422  break;
423  default:
424  return;
425  }
426 
427  va_list ap;
428  va_start(ap, format);
429  vfprintf(stderr, format, ap);
430  va_end(ap);
431 
432  fprintf(stderr, "\n");
433 }
434 
437 {
438  return debug_level;
439 }
440 
441 void
443 {
444  debug_level = level;
445 }
446 
447 void
448 update_state_by_keyval(int *state, int keyval)
449 {
450  if (state == NULL) {
451  return;
452  }
453 
454  if ((keyval >= '!' && keyval <= '/')
455  || (keyval >= ':' && keyval <= '@')
456  || (keyval >= '[' && keyval <= '`')
457  || (keyval >= '{' && keyval <= '~')
458  ) {
459  *state |= GDK_SHIFT_MASK;
460  }
461 }
462 
463 char* girara_escape_string(const char* value)
464 {
465  if (value == NULL) {
466  return NULL;
467  }
468 
469  GString* str = g_string_new("");
470  while (*value != '\0') {
471  const char c = *value++;
472  if (strchr("\\ \t\"\'", c) != NULL) {
473  g_string_append_c(str, '\\');
474  }
475  g_string_append_c(str, c);
476  }
477 
478  return g_string_free(str, FALSE);
479 }
480 
481 bool
482 girara_exec_with_argument_list(girara_session_t* session, girara_list_t* argument_list)
483 {
484  if (session == NULL || argument_list == NULL) {
485  return false;
486  }
487 
488  char* cmd = NULL;
489  girara_setting_get(session, "exec-command", &cmd);
490  if (cmd == NULL || strlen(cmd) == 0) {
491  girara_debug("exec-command is empty, executing directly.");
492  g_free(cmd);
493  cmd = NULL;
494  }
495 
496  bool dont_append_first_space = cmd == NULL;
497  GString* command = g_string_new(cmd ? cmd : "");
498  g_free(cmd);
499 
500  GIRARA_LIST_FOREACH(argument_list, char*, iter, value)
501  if (dont_append_first_space == false) {
502  g_string_append_c(command, ' ');
503  }
504  dont_append_first_space = false;
505  char* tmp = g_shell_quote(value);
506  g_string_append(command, tmp);
507  g_free(tmp);
508  GIRARA_LIST_FOREACH_END(argument_list, char*, iter, value);
509 
510  GError* error = NULL;
511  girara_info("executing: %s", command->str);
512  gboolean ret = g_spawn_command_line_async(command->str, &error);
513  if (error != NULL) {
514  girara_warning("Failed to execute command: %s", error->message);
515  girara_notify(session, GIRARA_ERROR, _("Failed to execute command: %s"), error->message);
516  g_error_free(error);
517  }
518 
519  g_string_free(command, TRUE);
520 
521  return ret;
522 }