main.c 11 KB
Newer Older
1
/* SPDX-License-Identifier: Zlib */
2

3 4 5 6
#ifdef GTKOSXAPPLICATION
#include <gtkosxapplication.h>
#endif

7
#include <girara/settings.h>
Sebastian Ramacher's avatar
Sebastian Ramacher committed
8 9
#include <girara/log.h>

10 11
#include <glib/gi18n.h>
#include <glib/gstdio.h>
12
#include <errno.h>
13
#include <limits.h>
Moritz Lipp's avatar
Moritz Lipp committed
14
#include <locale.h>
15 16 17
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
18 19

#include "zathura.h"
20
#include "utils.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
21
#include "dbus-interface.h"
22
#ifdef WITH_SYNCTEX
23
#include "synctex.h"
24
#endif
valoq's avatar
valoq committed
25

Sebastian Ramacher's avatar
Sebastian Ramacher committed
26 27 28
/* Init locale */
static void
init_locale(void)
29
{
30 31 32 33
  setlocale(LC_ALL, "");
  bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
  textdomain(GETTEXT_PACKAGE);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
34 35 36 37 38 39 40
}

/* Set log level */
static void
set_log_level(const char* loglevel)
{
  if (loglevel == NULL || g_strcmp0(loglevel, "info") == 0) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
41
    girara_set_log_level(GIRARA_INFO);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
42
  } else if (g_strcmp0(loglevel, "warning") == 0) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
43
    girara_set_log_level(GIRARA_WARNING);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
44
  } else if (g_strcmp0(loglevel, "error") == 0) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
45
    girara_set_log_level(GIRARA_ERROR);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
46 47 48 49 50 51 52
  }
}

/* Handle synctex forward synchronization */
#ifdef WITH_SYNCTEX
static int
run_synctex_forward(const char* synctex_fwd, const char* filename,
53
                    int synctex_pid)
Sebastian Ramacher's avatar
Sebastian Ramacher committed
54 55 56 57 58 59 60 61 62 63 64 65 66 67
{
  GFile* file = g_file_new_for_commandline_arg(filename);
  if (file == NULL) {
    girara_error("Unable to handle argument '%s'.", filename);
    return -1;
  }

  char* real_path = g_file_get_path(file);
  g_object_unref(file);
  if (real_path == NULL) {
    girara_error("Failed to determine path for '%s'", filename);
    return -1;
  }

68 69
  int   line       = 0;
  int   column     = 0;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
70 71 72 73 74 75 76
  char* input_file = NULL;
  if (synctex_parse_input(synctex_fwd, &input_file, &line, &column) == false) {
    girara_error("Failed to parse argument to --synctex-forward.");
    g_free(real_path);
    return -1;
  }

77 78
  const int ret = zathura_dbus_synctex_position(real_path, input_file, line,
                                                column, synctex_pid);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
79 80 81 82 83
  g_free(input_file);
  g_free(real_path);

  if (ret == -1) {
    /* D-Bus or SyncTeX failed */
84 85
    girara_error(
      "Got no usable data from SyncTeX or D-Bus failed in some way.");
Sebastian Ramacher's avatar
Sebastian Ramacher committed
86 87 88 89 90 91 92 93
  }

  return ret;
}
#endif

static zathura_t*
init_zathura(const char* config_dir, const char* data_dir,
94
             const char* cache_dir, const char* plugin_path, char** argv,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
95
             const char* synctex_editor, Window embed)
Sebastian Ramacher's avatar
Sebastian Ramacher committed
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
{
  /* create zathura session */
  zathura_t* zathura = zathura_create();
  if (zathura == NULL) {
    return NULL;
  }

  zathura_set_xid(zathura, embed);
  zathura_set_config_dir(zathura, config_dir);
  zathura_set_data_dir(zathura, data_dir);
  zathura_set_cache_dir(zathura, cache_dir);
  zathura_set_plugin_dir(zathura, plugin_path);
  zathura_set_argv(zathura, argv);

  /* Init zathura */
  if (zathura_init(zathura) == false) {
    zathura_free(zathura);
    return NULL;
  }

  if (synctex_editor != NULL) {
117 118
    girara_setting_set(zathura->ui.session, "synctex-editor-command",
                       synctex_editor);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
119 120 121 122 123 124 125
  }

  return zathura;
}


/* main function */
126
GIRARA_VISIBLE int
Sebastian Ramacher's avatar
Sebastian Ramacher committed
127 128
main(int argc, char* argv[])
{
valoq's avatar
valoq committed
129

Sebastian Ramacher's avatar
Sebastian Ramacher committed
130
  init_locale();
131

132 133 134
  /* parse command line arguments */
  gchar* config_dir     = NULL;
  gchar* data_dir       = NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
135
  gchar* cache_dir      = NULL;
136 137 138 139
  gchar* plugin_path    = NULL;
  gchar* loglevel       = NULL;
  gchar* password       = NULL;
  gchar* synctex_editor = NULL;
140
  gchar* synctex_fwd    = NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
141
  gchar* mode           = NULL;
B E's avatar
B E committed
142
  gchar* bookmark_name  = NULL;
Christoph Stelz's avatar
Christoph Stelz committed
143
  gchar* search_string  = NULL;
144 145 146 147
  bool   forkback       = false;
  bool   print_version  = false;
  int    page_number    = ZATHURA_PAGE_NUMBER_UNSPECIFIED;
  int    synctex_pid    = -1;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
148
  Window embed          = 0;
149 150

  GOptionEntry entries[] = {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
151
    { "reparent",               'e',  0, G_OPTION_ARG_INT,      &embed,          _("Reparents to window specified by xid (X11)"),        "xid"  },
152 153
    { "config-dir",             'c',  0, G_OPTION_ARG_FILENAME, &config_dir,     _("Path to the config directory"),                      "path" },
    { "data-dir",               'd',  0, G_OPTION_ARG_FILENAME, &data_dir,       _("Path to the data directory"),                        "path" },
Sebastian Ramacher's avatar
Sebastian Ramacher committed
154
    { "cache-dir",              '\0', 0, G_OPTION_ARG_FILENAME, &cache_dir,      _("Path to the cache directory"),                       "path"},
155 156 157 158
    { "plugins-dir",            'p',  0, G_OPTION_ARG_STRING,   &plugin_path,    _("Path to the directories containing plugins"),        "path" },
    { "fork",                   '\0', 0, G_OPTION_ARG_NONE,     &forkback,       _("Fork into the background"),                          NULL },
    { "password",               'w',  0, G_OPTION_ARG_STRING,   &password,       _("Document password"),                                 "password" },
    { "page",                   'P',  0, G_OPTION_ARG_INT,      &page_number,    _("Page number to go to"),                              "number" },
159
    { "log-level",              'l',  0, G_OPTION_ARG_STRING,   &loglevel,       _("Log level (debug, info, warning, error)"),           "level" },
160 161
    { "version",                'v',  0, G_OPTION_ARG_NONE,     &print_version,  _("Print version information"),                         NULL },
    { "synctex-editor-command", 'x',  0, G_OPTION_ARG_STRING,   &synctex_editor, _("Synctex editor (forwarded to the synctex command)"), "cmd" },
Sebastian Ramacher's avatar
Sebastian Ramacher committed
162
    { "synctex-forward",        '\0', 0, G_OPTION_ARG_STRING,   &synctex_fwd,    _("Move to given synctex position"),                    "position" },
163
    { "synctex-pid",            '\0', 0, G_OPTION_ARG_INT,      &synctex_pid,    _("Highlight given position in the given process"),     "pid" },
Sebastian Ramacher's avatar
Sebastian Ramacher committed
164
    { "mode",                   '\0', 0, G_OPTION_ARG_STRING,   &mode,           _("Start in a non-default mode"),                       "mode" },
B E's avatar
B E committed
165
    { "bookmark",               'b',  0, G_OPTION_ARG_STRING,   &bookmark_name,  _("Bookmark to go to"),                                 "bookmark" },
Christoph Stelz's avatar
Christoph Stelz committed
166
    { "find",                   'f',  0, G_OPTION_ARG_STRING,   &search_string,  _("Search for the given phrase and display results"),   "string" },
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
    { NULL, '\0', 0, 0, NULL, NULL, NULL }
  };

  GOptionContext* context = g_option_context_new(" [file1] [file2] [...]");
  g_option_context_add_main_entries(context, entries, NULL);

  GError* error = NULL;
  if (g_option_context_parse(context, &argc, &argv, &error) == false) {
    girara_error("Error parsing command line arguments: %s\n", error->message);
    g_option_context_free(context);
    g_error_free(error);

    return -1;
  }
  g_option_context_free(context);

Sebastian Ramacher's avatar
Sebastian Ramacher committed
183
  int ret = 0;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
184
  set_log_level(loglevel);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
185

186
#ifdef WITH_SYNCTEX
Sebastian Ramacher's avatar
Sebastian Ramacher committed
187
  /* handle synctex forward synchronization */
188 189
  if (synctex_fwd != NULL) {
    if (argc != 2) {
190 191
      girara_error("Too many arguments or missing filename while running with "
                   "--synctex-forward");
Sebastian Ramacher's avatar
Sebastian Ramacher committed
192 193
      ret = -1;
      goto free_and_ret;
194 195
    }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
196
    ret = run_synctex_forward(synctex_fwd, argv[1], synctex_pid);
197 198
    if (ret > 0) {
      /* Instance found. */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
199 200
      ret = 0;
      goto free_and_ret;
201 202 203
    }
    else if (ret < 0) {
      /* Error occurred. */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
204 205
      ret = -1;
      goto free_and_ret;
206
    }
207

208
    girara_debug("No instance found. Starting new one.");
209
  }
210 211
#else
  if (synctex_fwd != NULL || synctex_editor != NULL || synctex_pid != -1) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
212
    girara_error("Built without synctex support, but synctex specific option was specified.");
213 214 215
    ret = -1;
    goto free_and_ret;
  }
216
#endif
217

Sebastian Ramacher's avatar
Sebastian Ramacher committed
218
  /* check mode */
219 220
  if (mode != NULL && g_strcmp0(mode, "presentation") != 0 &&
      g_strcmp0(mode, "fullscreen") != 0) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
221
    girara_error("Invalid argument for --mode: %s", mode);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
222 223
    ret = -1;
    goto free_and_ret;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
224
  }
225

226 227 228 229 230 231 232
  /* g_option_context_parse has some funny (documented) behavior:
   * * for "-- a b c" you get no -- in argv
   * * for "-- --" you get -- in argv twice
   * * for "-- -a" you get -- in argv
   *
   * So if there is one -- in argv, we need to ignore it. */
  const bool has_double_dash = argc > 1 && g_strcmp0(argv[1], "--") == 0;
233
  const int  file_idx_base   = has_double_dash ? 2 : 1;
234 235

  int file_idx = argc > file_idx_base ? file_idx_base : 0;
236
  /* Fork instances for other files. */
237 238
  if (print_version == false && argc > file_idx_base + 1) {
    for (int idx = file_idx_base + 1; idx < argc; ++idx) {
239 240 241 242
      const pid_t pid = fork();
      if (pid == 0) { /* child */
        file_idx = idx;
        if (setsid() == -1) {
243 244
          girara_error("Could not start new process group: %s",
                       strerror(errno));
Sebastian Ramacher's avatar
Sebastian Ramacher committed
245 246
          ret = -1;
          goto free_and_ret;
247
        }
248
        break;
249 250
      }
      else if (pid < 0) { /* error */
251
        girara_error("Could not fork: %s", strerror(errno));
Sebastian Ramacher's avatar
Sebastian Ramacher committed
252 253
        ret = -1;
        goto free_and_ret;
254 255 256 257
      }
    }
  }

258
  /* Fork into the background if the user really wants to ... */
259 260
  if (print_version == false && forkback == true &&
      file_idx < file_idx_base + 1) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
261
    const pid_t pid = fork();
262
    if (pid > 0) { /* parent */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
263
      goto free_and_ret;
264 265
    }
    else if (pid < 0) { /* error */
266
      girara_error("Could not fork: %s", strerror(errno));
Sebastian Ramacher's avatar
Sebastian Ramacher committed
267 268
      ret = -1;
      goto free_and_ret;
269 270
    }

271 272
    if (setsid() == -1) {
      girara_error("Could not start new process group: %s", strerror(errno));
Sebastian Ramacher's avatar
Sebastian Ramacher committed
273 274
      ret = -1;
      goto free_and_ret;
275
    }
276 277
  }

278
  /* Initialize GTK+ */
279 280
  gtk_init(&argc, &argv);

281
  /* Create zathura session */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
282
  zathura_t* zathura = init_zathura(config_dir, data_dir, cache_dir,
283
                                    plugin_path, argv, synctex_editor, embed);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
284
  if (zathura == NULL) {
285
    girara_error("Could not initialize zathura.");
Sebastian Ramacher's avatar
Sebastian Ramacher committed
286 287
    ret = -1;
    goto free_and_ret;
288 289 290 291 292 293 294
  }

  /* Print version */
  if (print_version == true) {
    char* string = zathura_get_version_string(zathura, false);
    if (string != NULL) {
      fprintf(stdout, "%s\n", string);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
295
      g_free(string);
296 297 298
    }
    zathura_free(zathura);

Sebastian Ramacher's avatar
Sebastian Ramacher committed
299
    goto free_and_ret;
300 301 302
  }

  /* open document if passed */
303
  if (file_idx != 0) {
Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
304
    if (page_number > 0) {
305
      --page_number;
Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
306
    }
B E's avatar
B E committed
307 308 309 310 311 312 313
    document_open_idle(zathura, argv[file_idx], password, page_number,
                       mode, synctex_fwd, bookmark_name, search_string);
  } else if (bookmark_name != NULL) {
    girara_error("Can not use bookmark argument when no file is given");
    ret = -1;
    zathura_free(zathura);
    goto free_and_ret;
Christoph Stelz's avatar
Christoph Stelz committed
314 315 316 317 318
  } else if (search_string != NULL) {
    girara_error("Can not use find argument when no file is given");
    ret = -1;
    zathura_free(zathura);
    goto free_and_ret;
319 320
  }

dr_selump14's avatar
dr_selump14 committed
321 322 323 324 325 326 327 328 329 330 331 332
#ifdef GTKOSXAPPLICATION
  GtkosxApplication *zathuraApp;
  zathuraApp  = g_object_new (GTKOSX_TYPE_APPLICATION, NULL);
  gtkosx_application_set_use_quartz_accelerators (zathuraApp, FALSE);
  gtkosx_application_ready (zathuraApp);
  {
    const gchar *id = gtkosx_application_get_bundle_id ();
    if (id != NULL)
      {
        g_print ("TestIntegration Error! Bundle Has ID %s\n", id);
      }
  }
333
#endif //GTKOSXAPPLICATION
334
  /* run zathura */
335 336
  gtk_main();

337
  /* free zathura */
338 339
  zathura_free(zathura);

Sebastian Ramacher's avatar
Sebastian Ramacher committed
340 341 342 343 344 345 346 347 348 349
free_and_ret:
  g_free(config_dir);
  g_free(data_dir);
  g_free(cache_dir);
  g_free(plugin_path);
  g_free(loglevel);
  g_free(password);
  g_free(synctex_editor);
  g_free(synctex_fwd);
  g_free(mode);
B E's avatar
B E committed
350
  g_free(bookmark_name);
Christoph Stelz's avatar
Christoph Stelz committed
351
  g_free(search_string);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
352 353

  return ret;
354
}