zathura
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
completion.c
Go to the documentation of this file.
1 /* See LICENSE file for license and copyright information */
2 
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include <libgen.h>
8 #include <glib/gi18n.h>
9 
10 #include "bookmarks.h"
11 #include "document.h"
12 #include "completion.h"
13 #include "utils.h"
14 #include "page.h"
15 
16 #include <girara/session.h>
17 #include <girara/settings.h>
18 #include <girara/completion.h>
19 #include <girara/utils.h>
20 #include <girara/datastructures.h>
21 
22 static int
23 compare_case_insensitive(const char* str1, const char* str2)
24 {
25  char* ustr1 = g_utf8_casefold(str1, -1);
26  char* ustr2 = g_utf8_casefold(str2, -1);
27  int res = g_utf8_collate(ustr1, ustr2);
28  g_free(ustr1);
29  g_free(ustr2);
30  return res;
31 }
32 
33 static girara_list_t*
34 list_files(zathura_t* zathura, const char* current_path, const char* current_file,
35  unsigned int current_file_length, bool is_dir, bool check_file_ext)
36 {
37  if (zathura == NULL || zathura->ui.session == NULL || current_path == NULL) {
38  return NULL;
39  }
40 
41  /* read directory */
42  GDir* dir = g_dir_open(current_path, 0, NULL);
43  if (dir == NULL) {
44  return NULL;
45  }
46 
47  girara_list_t* res = girara_sorted_list_new2((girara_compare_function_t)compare_case_insensitive,
48  (girara_free_function_t)g_free);
49 
50  bool show_hidden = false;
51  girara_setting_get(zathura->ui.session, "show-hidden", &show_hidden);
52  bool show_directories = true;
53  girara_setting_get(zathura->ui.session, "show-directories", &show_directories);
54 
55  /* read files */
56  char* name = NULL;
57  while ((name = (char*) g_dir_read_name(dir)) != NULL) {
58  char* e_name = g_filename_display_name(name);
59  if (e_name == NULL) {
60  goto error_free;
61  }
62 
63  size_t e_length = strlen(e_name);
64 
65  if (show_hidden == false && e_name[0] == '.') {
66  g_free(e_name);
67  continue;
68  }
69 
70  if ((current_file_length > e_length) || strncmp(current_file, e_name, current_file_length)) {
71  g_free(e_name);
72  continue;
73  }
74 
75  char* tmp = "/";
76  if (is_dir == true || g_strcmp0(current_path, "/") == 0) {
77  tmp = "";
78  };
79 
80  char* full_path = g_strdup_printf("%s%s%s", current_path, tmp, e_name);
81 
82  if (g_file_test(full_path, G_FILE_TEST_IS_DIR) == true) {
83  if (show_directories == false) {
84  g_free(e_name);
85  g_free(full_path);
86  continue;
87  }
88  girara_list_append(res, full_path);
89  } else if (check_file_ext == false || file_valid_extension(zathura, full_path) == true) {
90  girara_list_append(res, full_path);
91  } else {
92  g_free(full_path);
93  }
94  g_free(e_name);
95  }
96 
97  g_dir_close(dir);
98 
99  if (girara_list_size(res) == 1) {
100  char* path = girara_list_nth(res, 0);
101  if (g_file_test(path, G_FILE_TEST_IS_DIR) == true) {
102  char* newpath = g_strdup_printf("%s/", path);
103  girara_list_clear(res);
104  girara_list_append(res, newpath);
105  }
106  }
107 
108  return res;
109 
110 error_free:
111  girara_list_free(res);
112  return NULL;
113 }
114 
115 static girara_completion_t*
116 list_files_for_cc(zathura_t* zathura, const char* input, bool check_file_ext)
117 {
118  girara_completion_t* completion = girara_completion_init();
119  girara_completion_group_t* group = girara_completion_group_create(zathura->ui.session, NULL);
120 
121  gchar* path = NULL;
122  gchar* current_path = NULL;
123 
124  if (completion == NULL || group == NULL) {
125  goto error_free;
126  }
127 
128  path = girara_fix_path(input);
129  if (path == NULL) {
130  goto error_free;
131  }
132 
133  /* If the path does not begin with a slash we update the path with the current
134  * working directory */
135  if (strlen(path) == 0 || path[0] != '/') {
136  long path_max;
137 #ifdef PATH_MAX
138  path_max = PATH_MAX;
139 #else
140  path_max = pathconf(path,_PC_PATH_MAX);
141  if (path_max <= 0)
142  path_max = 4096;
143 #endif
144 
145  char cwd[path_max];
146  if (getcwd(cwd, path_max) == NULL) {
147  goto error_free;
148  }
149 
150  char* tmp_path = g_strdup_printf("%s/%s", cwd, path);
151 
152  g_free(path);
153  path = tmp_path;
154  }
155 
156  /* Append a slash if the given argument is a directory */
157  bool is_dir = (path[strlen(path) - 1] == '/') ? true : false;
158  if ((g_file_test(path, G_FILE_TEST_IS_DIR) == TRUE) && !is_dir) {
159  char* tmp_path = g_strdup_printf("%s/", path);
160  g_free(path);
161  path = tmp_path;
162  is_dir = true;
163  }
164 
165  /* get current path */
166  char* tmp = g_strdup(path);
167  current_path = is_dir ? g_strdup(tmp) : g_strdup(dirname(tmp));
168  g_free(tmp);
169 
170  /* get current file */
171  gchar* current_file = is_dir ? "" : basename(path);
172  int current_file_length = strlen(current_file);
173 
174  /* read directory */
175  if (g_file_test(current_path, G_FILE_TEST_IS_DIR) == TRUE) {
176  girara_list_t* names = list_files(zathura, current_path, current_file, current_file_length, is_dir, check_file_ext);
177  if (!names) {
178  goto error_free;
179  }
180 
181  GIRARA_LIST_FOREACH(names, const char*, iter, file)
182  girara_completion_group_add_element(group, file, NULL);
183  GIRARA_LIST_FOREACH_END(names, const char*, iter, file);
184  girara_list_free(names);
185  }
186 
187  g_free(path);
188  g_free(current_path);
189 
190  girara_completion_add_group(completion, group);
191 
192  return completion;
193 
194 error_free:
195 
196  if (completion) {
197  girara_completion_free(completion);
198  }
199  if (group) {
200  girara_completion_group_free(group);
201  }
202 
203  g_free(current_path);
204  g_free(path);
205 
206  return NULL;
207 }
208 
209 girara_completion_t*
210 cc_open(girara_session_t* session, const char* input)
211 {
212  g_return_val_if_fail(session != NULL, NULL);
213  g_return_val_if_fail(session->global.data != NULL, NULL);
214  zathura_t* zathura = session->global.data;
215 
216  return list_files_for_cc(zathura, input, true);
217 }
218 
219 girara_completion_t*
220 cc_write(girara_session_t* session, const char* input)
221 {
222  g_return_val_if_fail(session != NULL, NULL);
223  g_return_val_if_fail(session->global.data != NULL, NULL);
224  zathura_t* zathura = session->global.data;
225 
226  return list_files_for_cc(zathura, input, false);
227 }
228 
229 girara_completion_t*
230 cc_bookmarks(girara_session_t* session, const char* input)
231 {
232  if (input == NULL) {
233  return NULL;
234  }
235 
236  g_return_val_if_fail(session != NULL, NULL);
237  g_return_val_if_fail(session->global.data != NULL, NULL);
238  zathura_t* zathura = session->global.data;
239 
240  girara_completion_t* completion = girara_completion_init();
241  girara_completion_group_t* group = girara_completion_group_create(session, NULL);
242 
243  if (completion == NULL || group == NULL) {
244  goto error_free;
245  }
246 
247  const size_t input_length = strlen(input);
248  GIRARA_LIST_FOREACH(zathura->bookmarks.bookmarks, zathura_bookmark_t*, iter, bookmark)
249  if (input_length <= strlen(bookmark->id) && !strncmp(input, bookmark->id, input_length)) {
250  gchar* paged = g_strdup_printf(_("Page %d"), bookmark->page);
251  girara_completion_group_add_element(group, bookmark->id, paged);
252  g_free(paged);
253  }
254  GIRARA_LIST_FOREACH_END(zathura->bookmarks.bookmarks, zathura_bookmark_t*, iter, bookmark);
255 
256  girara_completion_add_group(completion, group);
257 
258  return completion;
259 
260 error_free:
261 
262  if (completion) {
263  girara_completion_free(completion);
264  }
265 
266  if (group) {
267  girara_completion_group_free(group);
268  }
269 
270  return NULL;
271 }
272 
273 girara_completion_t*
274 cc_export(girara_session_t* session, const char* input)
275 {
276  g_return_val_if_fail(session != NULL, NULL);
277  g_return_val_if_fail(session->global.data != NULL, NULL);
278  zathura_t* zathura = session->global.data;
279 
280  if (input == NULL || zathura->document == NULL) {
281  goto error_ret;
282  }
283 
284  girara_completion_t* completion = NULL;
285  girara_completion_group_t* attachment_group = NULL;
286  girara_completion_group_t* image_group = NULL;
287 
288  completion = girara_completion_init();
289  if (completion == NULL) {
290  goto error_free;
291  }
292 
293  attachment_group = girara_completion_group_create(session, _("Attachments"));
294  if (attachment_group == NULL) {
295  goto error_free;
296  }
297 
298  /* add attachments */
299  const size_t input_length = strlen(input);
300  girara_list_t* attachments = zathura_document_attachments_get(zathura->document, NULL);
301  if (attachments != NULL) {
302  bool added = false;
303 
304  GIRARA_LIST_FOREACH(attachments, const char*, iter, attachment)
305  if (input_length <= strlen(attachment) && !strncmp(input, attachment, input_length)) {
306  char* attachment_string = g_strdup_printf("attachment-%s", attachment);
307  girara_completion_group_add_element(attachment_group, attachment_string, NULL);
308  g_free(attachment_string);
309  added = true;
310  }
311  GIRARA_LIST_FOREACH_END(zathura->bookmarks.bookmarks, zathura_bookmark_t*, iter, bookmark);
312 
313  if (added == true) {
314  girara_completion_add_group(completion, attachment_group);
315  } else {
316  girara_completion_group_free(attachment_group);
317  attachment_group = NULL;
318  }
319 
320  girara_list_free(attachments);
321  }
322 
323  /* add images */
324  image_group = girara_completion_group_create(session, _("Images"));
325  if (image_group == NULL) {
326  goto error_free;
327  }
328 
329  bool added = false;
330 
331  unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
332  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
333  zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
334  if (page == NULL) {
335  continue;
336  }
337 
338  girara_list_t* images = zathura_page_images_get(page, NULL);
339  if (images != NULL) {
340  unsigned int image_number = 1;
341  GIRARA_LIST_FOREACH(images, zathura_image_t*, iter, UNUSED(image))
342  char* image_string = g_strdup_printf("image-p%d-%d", page_id + 1, image_number);
343  girara_completion_group_add_element(image_group, image_string, NULL);
344  g_free(image_string);
345 
346  added = true;
347  image_number++;
348  GIRARA_LIST_FOREACH_END(images, zathura_image_t*, iter, image);
349  girara_list_free(images);
350  }
351  }
352 
353  if (added == true) {
354  girara_completion_add_group(completion, image_group);
355  } else {
356  girara_completion_group_free(image_group);
357  image_group = NULL;
358  }
359 
360  return completion;
361 
362 error_free:
363 
364  if (completion != NULL) {
365  girara_completion_free(completion);
366  }
367 
368  if (attachment_group != NULL) {
369  girara_completion_group_free(attachment_group);
370  }
371 
372  if (image_group != NULL) {
373  girara_completion_group_free(image_group);
374  }
375 
376 error_ret:
377 
378  return NULL;
379 }