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

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>

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;
  }

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;
}
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);
}

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

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);
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);
255

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

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;
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;
}