synctex.c 6.7 KB
Newer Older
1
2
/* See LICENSE file for license and copyright information */

Moritz Lipp's avatar
Moritz Lipp committed
3
4
#include <glib.h>

5
6
7
8
#include "synctex.h"
#include "zathura.h"
#include "page.h"
#include "document.h"
Moritz Lipp's avatar
Moritz Lipp committed
9
#include "utils.h"
10

Moritz Lipp's avatar
Moritz Lipp committed
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
enum {
  SYNCTEX_RESULT_BEGIN = 1,
  SYNCTEX_RESULT_END,
  SYNCTEX_PROP_PAGE,
  SYNCTEX_PROP_H,
  SYNCTEX_PROP_V,
  SYNCTEX_PROP_WIDTH,
  SYNCTEX_PROP_HEIGHT,
};

typedef struct token_s {
  const char* name;
  guint token;
} token_t;

static token_t scanner_tokens[] = {
  {"SyncTeX result begin", SYNCTEX_RESULT_BEGIN},
  {"SyncTeX result end", SYNCTEX_RESULT_END},
  {"Page:", SYNCTEX_PROP_PAGE},
  {"h:", SYNCTEX_PROP_H},
  {"v:", SYNCTEX_PROP_V},
  {"W:", SYNCTEX_PROP_WIDTH},
  {"H:", SYNCTEX_PROP_HEIGHT},
  {NULL, 0}
};

static GScannerConfig scanner_config = {
  .cset_skip_characters  = "\n\r",
  .cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z,
  .cset_identifier_nth   = G_CSET_a_2_z G_CSET_A_2_Z ": ",
  .cpair_comment_single  = NULL,
  .case_sensitive        = TRUE,
  .scan_identifier       = TRUE,
  .scan_symbols          = TRUE,
  .scan_float            = TRUE,
  .numbers_2_int         = TRUE,
};

49
50
51
void
synctex_edit(zathura_t* zathura, zathura_page_t* page, int x, int y)
{
Moritz Lipp's avatar
Moritz Lipp committed
52
53
54
  if (zathura == NULL || page == NULL) {
    return;
  }
55

Moritz Lipp's avatar
Moritz Lipp committed
56
57
  zathura_document_t* document = zathura_page_get_document(page);
  if (document == NULL) {
58
    return;
Moritz Lipp's avatar
Moritz Lipp committed
59
60
61
62
63
64
65
  }

  const char *filename = zathura_document_get_path(document);
  if (filename == NULL) {
    return;
  }

66
  char** argv = g_try_malloc0(sizeof(char*) * (zathura->synctex.editor != NULL ?
67
      6 : 4));
68
69
70
71
  if (argv == NULL) {
    return;
  }

72
73
74
  argv[0] = g_strdup("synctex");
  argv[1] = g_strdup("edit");
  argv[2] = g_strdup("-o");
75
76
  argv[3] = g_strdup_printf("%d:%d:%d:%s", zathura_page_get_index(page) + 1, x,
      y, filename);
Moritz Lipp's avatar
Moritz Lipp committed
77
  if (zathura->synctex.editor != NULL) {
78
79
    argv[4] = g_strdup("-x");
    argv[5] = g_strdup(zathura->synctex.editor);
80
81
  }

82
83
  g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
  g_strfreev(argv);
84
}
Moritz Lipp's avatar
Moritz Lipp committed
85
86

static double
Moritz Lipp's avatar
Moritz Lipp committed
87
88
scan_float(GScanner* scanner)
{
Moritz Lipp's avatar
Moritz Lipp committed
89
90
91
92
93
94
95
96
97
98
  switch (g_scanner_get_next_token(scanner)) {
    case G_TOKEN_FLOAT:
      return g_scanner_cur_value(scanner).v_float;
    case G_TOKEN_INT:
      return g_scanner_cur_value(scanner).v_int;
    default:
      return 0.0;
  }
}

99
girara_list_t*
100
synctex_rectangles_from_position(const char* filename, const char* position,
101
102
                                 unsigned int* page,
                                 girara_list_t** secondary_rects)
Moritz Lipp's avatar
Moritz Lipp committed
103
{
104
  if (filename == NULL || position == NULL || page == NULL) {
105
    return NULL;
106
107
  }

108
109
110
111
112
  char** argv = g_try_malloc0(sizeof(char*) * 6);
  if (argv == NULL) {
    return NULL;
  }

113
114
115
116
117
  argv[0] = g_strdup("synctex");
  argv[1] = g_strdup("view");
  argv[2] = g_strdup("-i");
  argv[3] = g_strdup(position);
  argv[4] = g_strdup("-o");
118
  argv[5] = g_strdup(filename);
Moritz Lipp's avatar
Moritz Lipp committed
119

120
  gint output = -1;
121
122
123
124
  bool ret = g_spawn_async_with_pipes(NULL, argv, NULL,
      G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, NULL, NULL,
      &output, NULL, NULL);
  g_strfreev(argv);
Moritz Lipp's avatar
Moritz Lipp committed
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

  if (ret == false) {
    return false;
  }

  GScanner* scanner = g_scanner_new(&scanner_config);
  token_t* tokens = scanner_tokens;
  while (tokens->name != NULL) {
    g_scanner_add_symbol(scanner, tokens->name, GINT_TO_POINTER(tokens->token));
    tokens++;
  }

  g_scanner_input_file(scanner, output);

  bool found_begin = false, found_end = false;
  while (found_begin == false && found_end == false) {
    switch (g_scanner_get_next_token(scanner)) {
      case G_TOKEN_EOF:
        found_end = true;
        break;

      case G_TOKEN_SYMBOL:
        switch (GPOINTER_TO_INT(g_scanner_cur_value(scanner).v_identifier)) {
          case SYNCTEX_RESULT_BEGIN:
            found_begin = true;
            break;
        }
        break;

      default:
        /* skip everything else */
        break;
    }
  }

160
161
162
  ret = false;
  unsigned int rpage = 0;
  unsigned int current_page = 0;
163
164
  girara_list_t* hitlist = girara_list_new2(g_free);
  girara_list_t* other_rects = girara_list_new2(g_free);
Moritz Lipp's avatar
Moritz Lipp committed
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
  zathura_rectangle_t* rectangle = NULL;

  while (found_end == false) {
    switch (g_scanner_get_next_token(scanner)) {
      case G_TOKEN_EOF:
        found_end = true;
        break;

      case G_TOKEN_SYMBOL:
        switch (GPOINTER_TO_INT(g_scanner_cur_value(scanner).v_identifier)) {
          case SYNCTEX_RESULT_END:
            found_end = true;
            break;

          case SYNCTEX_PROP_PAGE:
            if (g_scanner_get_next_token(scanner) == G_TOKEN_INT) {
181
              current_page = g_scanner_cur_value(scanner).v_int - 1;
182
183
              if (ret == false) {
                ret = true;
184
                rpage = current_page;
Moritz Lipp's avatar
Moritz Lipp committed
185
              }
186
187
188
189

              if (*page == current_page && rectangle != NULL) {
                girara_list_append(hitlist, rectangle);
                rectangle = NULL;
190
              } else if (rectangle != NULL) {
191
192
193
194
195
                synctex_page_rect_t* page_rect = g_try_malloc0(sizeof(synctex_page_rect_t));
                if (page_rect == NULL) {
                  continue;
                }

196
                page_rect->page = current_page;
197
                page_rect->rect = *rectangle;
198

199
                girara_list_append(other_rects, page_rect);
200
201
202
              }

              g_free(rectangle);
203
204
205
206
              rectangle = g_try_malloc0(sizeof(zathura_rectangle_t));
              if (rectangle == NULL) {
                continue;
              }
Moritz Lipp's avatar
Moritz Lipp committed
207
208
209
210
            }
            break;

          case SYNCTEX_PROP_H:
211
212
213
            if (rectangle != NULL) {
              rectangle->x1 = scan_float(scanner);
            }
Moritz Lipp's avatar
Moritz Lipp committed
214
215
216
            break;

          case SYNCTEX_PROP_V:
217
218
219
            if (rectangle != NULL) {
              rectangle->y2 = scan_float(scanner);
            }
Moritz Lipp's avatar
Moritz Lipp committed
220
221
222
            break;

          case SYNCTEX_PROP_WIDTH:
223
224
225
            if (rectangle != NULL) {
              rectangle->x2 = rectangle->x1 + scan_float(scanner);
            }
Moritz Lipp's avatar
Moritz Lipp committed
226
227
228
            break;

          case SYNCTEX_PROP_HEIGHT:
229
230
231
            if (rectangle != NULL) {
              rectangle->y1 = rectangle->y2 - scan_float(scanner);
            }
Moritz Lipp's avatar
Moritz Lipp committed
232
233
234
235
236
237
238
239
240
            break;
        }
        break;

      default:
        break;
    }
  }

241
  if (rectangle != NULL) {
242
    if (current_page == rpage) {
243
244
      girara_list_append(hitlist, rectangle);
    } else {
245
246
247
248
249
250
251
      synctex_page_rect_t* page_rect = g_try_malloc0(sizeof(synctex_page_rect_t));
      if (page_rect != NULL) {
        page_rect->page = current_page;
        page_rect->rect = *rectangle;
        girara_list_append(other_rects, page_rect);
        g_free(rectangle);
      }
252
    }
Moritz Lipp's avatar
Moritz Lipp committed
253
254
255
256
257
  }

  g_scanner_destroy(scanner);
  close(output);

258
259
260
261
262
263
264
265
266
  if (page != NULL) {
    *page = rpage;
  }
  if (secondary_rects != NULL) {
    *secondary_rects = other_rects;
  } else {
    girara_list_free(other_rects);
  }

267
  return hitlist;
Moritz Lipp's avatar
Moritz Lipp committed
268
}