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 <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  };
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 == NULL) {
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 == true) {
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*
468 girara_escape_string(const char* value)
469 {
470  if (value == NULL) {
471  return NULL;
472  }
473 
474  GString* str = g_string_new("");
475  while (*value != '\0') {
476  const char c = *value++;
477  if (strchr("\\ \t\"\'", c) != NULL) {
478  g_string_append_c(str, '\\');
479  }
480  g_string_append_c(str, c);
481  }
482 
483  return g_string_free(str, FALSE);
484 }
485 
486 bool
487 girara_exec_with_argument_list(girara_session_t* session, girara_list_t* argument_list)
488 {
489  if (session == NULL || argument_list == NULL) {
490  return false;
491  }
492 
493  char* cmd = NULL;
494  girara_setting_get(session, "exec-command", &cmd);
495  if (cmd == NULL || strlen(cmd) == 0) {
496  girara_debug("exec-command is empty, executing directly.");
497  g_free(cmd);
498  cmd = NULL;
499  }
500 
501  bool dont_append_first_space = cmd == NULL;
502  GString* command = g_string_new(cmd ? cmd : "");
503  g_free(cmd);
504 
505  GIRARA_LIST_FOREACH(argument_list, char*, iter, value)
506  if (dont_append_first_space == false) {
507  g_string_append_c(command, ' ');
508  }
509  dont_append_first_space = false;
510  char* tmp = g_shell_quote(value);
511  g_string_append(command, tmp);
512  g_free(tmp);
513  GIRARA_LIST_FOREACH_END(argument_list, char*, iter, value);
514 
515  GError* error = NULL;
516  girara_info("executing: %s", command->str);
517  gboolean ret = g_spawn_command_line_async(command->str, &error);
518  if (error != NULL) {
519  girara_warning("Failed to execute command: %s", error->message);
520  girara_notify(session, GIRARA_ERROR, _("Failed to execute command: %s"), error->message);
521  g_error_free(error);
522  }
523 
524  g_string_free(command, TRUE);
525 
526  return ret;
527 }