completion.c 9.93 KB
Newer Older
Moritz Lipp's avatar
Moritz Lipp committed
1
/* See LICENSE file for license and copyright information */
2
3
4
5

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
Moritz Lipp's avatar
Moritz Lipp committed
6
#include <unistd.h>
Moritz Lipp's avatar
Moritz Lipp committed
7
#include <libgen.h>
Moritz Lipp's avatar
Moritz Lipp committed
8
#include <glib/gi18n.h>
9

Sebastian Ramacher's avatar
Sebastian Ramacher committed
10
#include "bookmarks.h"
Moritz Lipp's avatar
Moritz Lipp committed
11
#include "document.h"
12
13
#include "completion.h"
#include "utils.h"
Moritz Lipp's avatar
Moritz Lipp committed
14
#include "page.h"
15

16
#include <girara/session.h>
17
#include <girara/settings.h>
18
19
20
21
#include <girara/completion.h>
#include <girara/utils.h>
#include <girara/datastructures.h>

Sebastian Ramacher's avatar
Sebastian Ramacher committed
22
23
24
25
26
27
28
29
30
31
32
static int
compare_case_insensitive(const char* str1, const char* str2)
{
  char* ustr1 = g_utf8_casefold(str1, -1);
  char* ustr2 = g_utf8_casefold(str2, -1);
  int res = g_utf8_collate(ustr1, ustr2);
  g_free(ustr1);
  g_free(ustr2);
  return res;
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
33
static girara_list_t*
34
list_files(zathura_t* zathura, const char* current_path, const char* current_file,
Moritz Lipp's avatar
Moritz Lipp committed
35
           unsigned int current_file_length, bool is_dir, bool check_file_ext)
Sebastian Ramacher's avatar
Sebastian Ramacher committed
36
{
37
38
39
40
  if (zathura == NULL || zathura->ui.session == NULL || current_path == NULL) {
    return NULL;
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
41
42
43
44
45
46
  /* read directory */
  GDir* dir = g_dir_open(current_path, 0, NULL);
  if (dir == NULL) {
    return NULL;
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
47
  girara_list_t* res = girara_sorted_list_new2((girara_compare_function_t)compare_case_insensitive,
Moritz Lipp's avatar
Moritz Lipp committed
48
                       (girara_free_function_t)g_free);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
49

50
51
  bool show_hidden = false;
  girara_setting_get(zathura->ui.session, "show-hidden", &show_hidden);
52
53
  bool show_directories = true;
  girara_setting_get(zathura->ui.session, "show-directories", &show_directories);
54

Sebastian Ramacher's avatar
Sebastian Ramacher committed
55
56
57
58
59
  /* read files */
  char* name = NULL;
  while ((name = (char*) g_dir_read_name(dir)) != NULL) {
    char* e_name   = g_filename_display_name(name);
    if (e_name == NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
60
      goto error_free;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
61
62
    }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
63
    size_t e_length = strlen(e_name);
64
65

    if (show_hidden == false && e_name[0] == '.') {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
66
      g_free(e_name);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
67
      continue;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
68
69
    }

70
71
72
73
    if ((current_file_length > e_length) || strncmp(current_file, e_name, current_file_length)) {
      g_free(e_name);
      continue;
    }
Moritz Lipp's avatar
Moritz Lipp committed
74
75
76
77
78
79
80

    char* tmp = "/";
    if (is_dir == true || g_strcmp0(current_path, "/") == 0) {
      tmp = "";
    };

    char* full_path = g_strdup_printf("%s%s%s", current_path, tmp, e_name);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
81
82

    if (g_file_test(full_path, G_FILE_TEST_IS_DIR) == true) {
83
84
85
86
87
      if (show_directories == false) {
        g_free(e_name);
        g_free(full_path);
        continue;
      }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
88
      girara_list_append(res, full_path);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
89
    } else if (check_file_ext == false || file_valid_extension(zathura, full_path) == true) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
90
91
92
93
94
95
96
97
      girara_list_append(res, full_path);
    } else {
      g_free(full_path);
    }
    g_free(e_name);
  }

  g_dir_close(dir);
98
99
100
101
102
103
104
105
106
107

  if (girara_list_size(res) == 1) {
    char* path = girara_list_nth(res, 0);
    if (g_file_test(path, G_FILE_TEST_IS_DIR) == true) {
      char* newpath = g_strdup_printf("%s/", path);
      girara_list_clear(res);
      girara_list_append(res, newpath);
    }
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
108
  return res;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
109
110
111
112

error_free:
  girara_list_free(res);
  return NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
113
114
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
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

Moritz Lipp's avatar
Moritz Lipp committed
121
122
123
  gchar* path         = NULL;
  gchar* current_path = NULL;

Moritz Lipp's avatar
Moritz Lipp committed
124
  if (completion == NULL || group == NULL) {
125
126
127
    goto error_free;
  }

Moritz Lipp's avatar
Moritz Lipp committed
128
  path = girara_fix_path(input);
Moritz Lipp's avatar
Moritz Lipp committed
129
  if (path == NULL) {
130
131
132
133
134
    goto error_free;
  }

  /* If the path does not begin with a slash we update the path with the current
   * working directory */
Moritz Lipp's avatar
Moritz Lipp committed
135
  if (strlen(path) == 0 || path[0] != '/') {
136
    long path_max;
137
138
139
140
#ifdef PATH_MAX
    path_max = PATH_MAX;
#else
    path_max = pathconf(path,_PC_PATH_MAX);
141
    if (path_max <= 0)
142
143
144
145
      path_max = 4096;
#endif

    char cwd[path_max];
Sebastian Ramacher's avatar
Sebastian Ramacher committed
146
147
148
    if (getcwd(cwd, path_max) == NULL) {
      goto error_free;
    }
149
150
151
152
153
154
155

    char* tmp_path = g_strdup_printf("%s/%s", cwd, path);

    g_free(path);
    path = tmp_path;
  }

Moritz Lipp's avatar
Moritz Lipp committed
156
  /* Append a slash if the given argument is a directory */
Moritz Lipp's avatar
Moritz Lipp committed
157
158
  bool is_dir = (path[strlen(path) - 1] == '/') ? true : false;
  if ((g_file_test(path, G_FILE_TEST_IS_DIR) == TRUE) && !is_dir) {
Moritz Lipp's avatar
Moritz Lipp committed
159
160
161
    char* tmp_path = g_strdup_printf("%s/", path);
    g_free(path);
    path = tmp_path;
Moritz Lipp's avatar
Moritz Lipp committed
162
    is_dir = true;
Moritz Lipp's avatar
Moritz Lipp committed
163
164
  }

Moritz Lipp's avatar
Moritz Lipp committed
165
166
167
168
169
170
171
  /* get current path */
  char* tmp    = g_strdup(path);
  current_path = is_dir ? g_strdup(tmp) : g_strdup(dirname(tmp));
  g_free(tmp);

  /* get current file */
  gchar* current_file     = is_dir ? "" : basename(path);
Moritz Lipp's avatar
Moritz Lipp committed
172
173
  int current_file_length = strlen(current_file);

174
  /* read directory */
Moritz Lipp's avatar
Moritz Lipp committed
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);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
177
    if (!names) {
178
179
180
      goto error_free;
    }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
181
    GIRARA_LIST_FOREACH(names, const char*, iter, file)
Moritz Lipp's avatar
Moritz Lipp committed
182
    girara_completion_group_add_element(group, file, NULL);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
183
184
    GIRARA_LIST_FOREACH_END(names, const char*, iter, file);
    girara_list_free(names);
185
186
187
  }

  g_free(path);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
188
  g_free(current_path);
189
190
191
192
193
194
195
196
197
198
199
200
201
202

  girara_completion_add_group(completion, group);

  return completion;

error_free:

  if (completion) {
    girara_completion_free(completion);
  }
  if (group) {
    girara_completion_group_free(group);
  }

Moritz Lipp's avatar
Moritz Lipp committed
203
  g_free(current_path);
204
205
206
207
  g_free(path);

  return NULL;
}
Sebastian Ramacher's avatar
Sebastian Ramacher committed
208

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
girara_completion_t*
cc_open(girara_session_t* session, const char* input)
{
  g_return_val_if_fail(session != NULL, NULL);
  g_return_val_if_fail(session->global.data != NULL, NULL);
  zathura_t* zathura = session->global.data;

  return list_files_for_cc(zathura, input, true);
}

girara_completion_t*
cc_write(girara_session_t* session, const char* input)
{
  g_return_val_if_fail(session != NULL, NULL);
  g_return_val_if_fail(session->global.data != NULL, NULL);
  zathura_t* zathura = session->global.data;

  return list_files_for_cc(zathura, input, false);
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
229
girara_completion_t*
230
cc_bookmarks(girara_session_t* session, const char* input)
Sebastian Ramacher's avatar
Sebastian Ramacher committed
231
{
Sebastian Ramacher's avatar
Sebastian Ramacher committed
232
233
234
235
  if (input == NULL) {
    return NULL;
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
236
237
238
239
240
241
242
  g_return_val_if_fail(session != NULL, NULL);
  g_return_val_if_fail(session->global.data != NULL, NULL);
  zathura_t* zathura = session->global.data;

  girara_completion_t* completion  = girara_completion_init();
  girara_completion_group_t* group = girara_completion_group_create(session, NULL);

Moritz Lipp's avatar
Moritz Lipp committed
243
244
245
246
  if (completion == NULL || group == NULL) {
    goto error_free;
  }

247
  const size_t input_length = strlen(input);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
248
  GIRARA_LIST_FOREACH(zathura->bookmarks.bookmarks, zathura_bookmark_t*, iter, bookmark)
Moritz Lipp's avatar
Moritz Lipp committed
249
250
251
252
253
  if (input_length <= strlen(bookmark->id) && !strncmp(input, bookmark->id, input_length)) {
    gchar* paged = g_strdup_printf(_("Page %d"), bookmark->page);
    girara_completion_group_add_element(group, bookmark->id, paged);
    g_free(paged);
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
254
  GIRARA_LIST_FOREACH_END(zathura->bookmarks.bookmarks, zathura_bookmark_t*, iter, bookmark);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
255

Moritz Lipp's avatar
Moritz Lipp committed
256
257
  girara_completion_add_group(completion, group);

Sebastian Ramacher's avatar
Sebastian Ramacher committed
258
  return completion;
Moritz Lipp's avatar
Moritz Lipp committed
259
260
261
262
263
264
265
266
267
268
269
270

error_free:

  if (completion) {
    girara_completion_free(completion);
  }

  if (group) {
    girara_completion_group_free(group);
  }

  return NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
271
}
Sebastian Ramacher's avatar
Sebastian Ramacher committed
272
273
274
275
276
277
278
279

girara_completion_t*
cc_export(girara_session_t* session, const char* input)
{
  g_return_val_if_fail(session != NULL, NULL);
  g_return_val_if_fail(session->global.data != NULL, NULL);
  zathura_t* zathura = session->global.data;

Moritz Lipp's avatar
Moritz Lipp committed
280
281
282
  if (input == NULL || zathura->document == NULL) {
    goto error_ret;
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
283

Moritz Lipp's avatar
Moritz Lipp committed
284
285
286
287
288
289
  girara_completion_t* completion             = NULL;
  girara_completion_group_t* attachment_group = NULL;
  girara_completion_group_t* image_group      = NULL;

  completion = girara_completion_init();
  if (completion == NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
290
291
292
    goto error_free;
  }

Moritz Lipp's avatar
Moritz Lipp committed
293
294
295
296
297
298
  attachment_group = girara_completion_group_create(session, _("Attachments"));
  if (attachment_group == NULL) {
    goto error_free;
  }

  /* add attachments */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
299
  const size_t input_length = strlen(input);
300
  girara_list_t* attachments = zathura_document_attachments_get(zathura->document, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
301
302
303
304
  if (attachments != NULL) {
    bool added = false;

    GIRARA_LIST_FOREACH(attachments, const char*, iter, attachment)
Moritz Lipp's avatar
Moritz Lipp committed
305
306
307
308
309
310
    if (input_length <= strlen(attachment) && !strncmp(input, attachment, input_length)) {
      char* attachment_string = g_strdup_printf("attachment-%s", attachment);
      girara_completion_group_add_element(attachment_group, attachment_string, NULL);
      g_free(attachment_string);
      added = true;
    }
Moritz Lipp's avatar
Moritz Lipp committed
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
    GIRARA_LIST_FOREACH_END(zathura->bookmarks.bookmarks, zathura_bookmark_t*, iter, bookmark);

    if (added == true) {
      girara_completion_add_group(completion, attachment_group);
    } else {
      girara_completion_group_free(attachment_group);
      attachment_group = NULL;
    }

    girara_list_free(attachments);
  }

  /* add images */
  image_group = girara_completion_group_create(session, _("Images"));
  if (image_group == NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
326
327
328
    goto error_free;
  }

Moritz Lipp's avatar
Moritz Lipp committed
329
330
331
332
333
334
335
  bool added = false;

  unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
    zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
    if (page == NULL) {
      continue;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
336
337
    }

Moritz Lipp's avatar
Moritz Lipp committed
338
339
340
    girara_list_t* images = zathura_page_images_get(page, NULL);
    if (images != NULL) {
      unsigned int image_number = 1;
Moritz Lipp's avatar
Moritz Lipp committed
341
      GIRARA_LIST_FOREACH(images, zathura_image_t*, iter, UNUSED(image))
Moritz Lipp's avatar
Moritz Lipp committed
342
343
344
      char* image_string = g_strdup_printf("image-p%d-%d", page_id + 1, image_number);
      girara_completion_group_add_element(image_group, image_string, NULL);
      g_free(image_string);
Moritz Lipp's avatar
Moritz Lipp committed
345

Moritz Lipp's avatar
Moritz Lipp committed
346
347
      added = true;
      image_number++;
Moritz Lipp's avatar
Moritz Lipp committed
348
349
350
351
352
353
354
355
356
357
358
359
      GIRARA_LIST_FOREACH_END(images, zathura_image_t*, iter, image);
      girara_list_free(images);
    }
  }

  if (added == true) {
    girara_completion_add_group(completion, image_group);
  } else {
    girara_completion_group_free(image_group);
    image_group = NULL;
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
360
361
362
363
  return completion;

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
364
  if (completion != NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
365
366
367
    girara_completion_free(completion);
  }

Moritz Lipp's avatar
Moritz Lipp committed
368
369
370
371
372
373
  if (attachment_group != NULL) {
    girara_completion_group_free(attachment_group);
  }

  if (image_group != NULL) {
    girara_completion_group_free(image_group);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
374
375
  }

Moritz Lipp's avatar
Moritz Lipp committed
376
377
error_ret:

Sebastian Ramacher's avatar
Sebastian Ramacher committed
378
379
  return NULL;
}