Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
synctex_parser_utils.c
Go to the documentation of this file.
1 /*
2 Copyright (c) 2008, 2009, 2010 , 2011 jerome DOT laurens AT u-bourgogne DOT fr
3 
4 This file is part of the SyncTeX package.
5 
6 Latest Revision: Tue Jun 14 08:23:30 UTC 2011
7 
8 Version: 1.17
9 
10 See synctex_parser_readme.txt for more details
11 
12 License:
13 --------
14 Permission is hereby granted, free of charge, to any person
15 obtaining a copy of this software and associated documentation
16 files (the "Software"), to deal in the Software without
17 restriction, including without limitation the rights to use,
18 copy, modify, merge, publish, distribute, sublicense, and/or sell
19 copies of the Software, and to permit persons to whom the
20 Software is furnished to do so, subject to the following
21 conditions:
22 
23 The above copyright notice and this permission notice shall be
24 included in all copies or substantial portions of the Software.
25 
26 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
28 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
30 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
31 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
33 OTHER DEALINGS IN THE SOFTWARE
34 
35 Except as contained in this notice, the name of the copyright holder
36 shall not be used in advertising or otherwise to promote the sale,
37 use or other dealings in this Software without prior written
38 authorization from the copyright holder.
39 
40 */
41 
42 /* In this file, we find all the functions that may depend on the operating system. */
43 
44 #include <synctex_parser_utils.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <stdarg.h>
48 #include <stdio.h>
49 
50 #include <limits.h>
51 #include <ctype.h>
52 #include <string.h>
53 
54 #include <sys/stat.h>
55 
56 #if defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
57 #define SYNCTEX_WINDOWS 1
58 #endif
59 
60 #if defined(__OS2__)
61 #define SYNCTEX_OS2 1
62 #endif
63 
64 #ifdef _WIN32_WINNT_WINXP
65 #define SYNCTEX_RECENT_WINDOWS 1
66 #endif
67 
68 #ifdef SYNCTEX_WINDOWS
69 #include <windows.h>
70 #endif
71 
72 void *_synctex_malloc(size_t size) {
73  void * ptr = malloc(size);
74  if(ptr) {
75 /* There used to be a switch to use bzero because it is more secure. JL */
76  memset(ptr,0, size);
77  }
78  return (void *)ptr;
79 }
80 
81 int _synctex_error(const char * reason,...) {
82  va_list arg;
83  int result;
84  va_start (arg, reason);
85 # ifdef SYNCTEX_RECENT_WINDOWS
86  {/* This code is contributed by William Blum.
87  As it does not work on some older computers,
88  the _WIN32 conditional here is replaced with a SYNCTEX_RECENT_WINDOWS one.
89  According to http://msdn.microsoft.com/en-us/library/aa363362(VS.85).aspx
90  Minimum supported client Windows 2000 Professional
91  Minimum supported server Windows 2000 Server
92  People running Windows 2K standard edition will not have OutputDebugStringA.
93  JL.*/
94  char *buff;
95  size_t len;
96  OutputDebugStringA("SyncTeX ERROR: ");
97  len = _vscprintf(reason, arg) + 1;
98  buff = (char*)malloc( len * sizeof(char) );
99  result = vsprintf(buff, reason, arg) +strlen("SyncTeX ERROR: ");
100  OutputDebugStringA(buff);
101  OutputDebugStringA("\n");
102  free(buff);
103  }
104 # else
105  result = fprintf(stderr,"SyncTeX ERROR: ");
106  result += vfprintf(stderr, reason, arg);
107  result += fprintf(stderr,"\n");
108 # endif
109  va_end (arg);
110  return result;
111 }
112 
113 /* strip the last extension of the given string, this string is modified! */
115  if(NULL != string){
116  char * last_component = NULL;
117  char * last_extension = NULL;
118  char * next = NULL;
119  /* first we find the last path component */
120  if(NULL == (last_component = strstr(string,"/"))){
121  last_component = string;
122  } else {
123  ++last_component;
124  while((next = strstr(last_component,"/"))){
125  last_component = next+1;
126  }
127  }
128 # if defined(SYNCTEX_WINDOWS) || defined(SYNCTEX_OS2)
129  /* On Windows, the '\' is also a path separator. */
130  while((next = strstr(last_component,"\\"))){
131  last_component = next+1;
132  }
133 # endif
134  /* then we find the last path extension */
135  if((last_extension = strstr(last_component,"."))){
136  ++last_extension;
137  while((next = strstr(last_extension,"."))){
138  last_extension = next+1;
139  }
140  --last_extension;/* back to the "." */
141  if(last_extension>last_component){/* filter out paths like ....my/dir/.hidden"*/
142  last_extension[0] = '\0';
143  }
144  }
145  }
146 }
147 
149 {
150  if (SYNCTEX_IS_DOT((*name_ref)[0]) && SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[1])) {
151  do {
152  (*name_ref) += 2;
153  while (SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[1])) {
154  ++(*name_ref);
155  }
156  } while(SYNCTEX_IS_DOT((*name_ref)[0]) && SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[1]));
157  return synctex_YES;
158  }
159  return synctex_NO;
160 }
161 
162 /* The base name is necessary to deal with the 2011 file naming convention...
163  * path is a '\0' terminated string
164  * The return value is the trailing part of the argument,
165  * just following the first occurrence of the regexp pattern "[^|/|\].[\|/]+".*/
166 const char * _synctex_base_name(const char *path) {
167  const char * ptr = path;
168  do {
170  return ptr;
171  }
172  do {
173  if (!*(++ptr)) {
174  return path;
175  }
176  } while (!SYNCTEX_IS_PATH_SEPARATOR(*ptr));
177  } while (*(++ptr));
178  return path;
179 }
180 
181 /* Compare two file names, windows is sometimes case insensitive... */
182 synctex_bool_t _synctex_is_equivalent_file_name(const char *lhs, const char *rhs) {
183  /* Remove the leading regex '(\./+)*' in both rhs and lhs */
186 next_character:
187  if (SYNCTEX_IS_PATH_SEPARATOR(*lhs)) {/* lhs points to a path separator */
188  if (!SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/* but not rhs */
189  return synctex_NO;
190  }
191  ++lhs;
192  ++rhs;
195  goto next_character;
196  } else if (SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/* rhs points to a path separator but not lhs */
197  return synctex_NO;
198  } else if (SYNCTEX_ARE_PATH_CHARACTERS_EQUAL(*lhs,*rhs)){/* uppercase do not match */
199  return synctex_NO;
200  } else if (!*lhs) {/* lhs is at the end of the string */
201  return *rhs ? synctex_NO : synctex_YES;
202  } else if(!*rhs) {/* rhs is at the end of the string but not lhs */
203  return synctex_NO;
204  }
205  ++lhs;
206  ++rhs;
207  goto next_character;
208 }
209 
211  if(!strlen(name)) {
212  return synctex_NO;
213  }
214 # if defined(SYNCTEX_WINDOWS) || defined(SYNCTEX_OS2)
215  if(strlen(name)>2) {
216  return (name[1]==':' && SYNCTEX_IS_PATH_SEPARATOR(name[2]))?synctex_YES:synctex_NO;
217  }
218  return synctex_NO;
219 # else
221 # endif
222 }
223 
224 /* We do not take care of UTF-8 */
225 const char * _synctex_last_path_component(const char * name) {
226  const char * c = name+strlen(name);
227  if(c>name) {
228  if(!SYNCTEX_IS_PATH_SEPARATOR(*c)) {
229  do {
230  --c;
231  if(SYNCTEX_IS_PATH_SEPARATOR(*c)) {
232  return c+1;
233  }
234  } while(c>name);
235  }
236  return c;/* the last path component is the void string*/
237  }
238  return c;
239 }
240 
241 int _synctex_copy_with_quoting_last_path_component(const char * src, char ** dest_ref, size_t size) {
242  const char * lpc;
243  if(src && dest_ref) {
244 # define dest (*dest_ref)
245  dest = NULL; /* Default behavior: no change and sucess. */
246  lpc = _synctex_last_path_component(src);
247  if(strlen(lpc)) {
248  if(strchr(lpc,' ') && lpc[0]!='"' && lpc[strlen(lpc)-1]!='"') {
249  /* We are in the situation where adding the quotes is allowed. */
250  /* Time to add the quotes. */
251  /* Consistency test: we must have dest+size>dest+strlen(dest)+2
252  * or equivalently: strlen(dest)+2<size (see below) */
253  if(strlen(src)<size) {
254  if((dest = (char *)malloc(size+2))) {
255  char * dpc = dest + (lpc-src); /* dpc is the last path component of dest. */
256  if(dest != strncpy(dest,src,size)) {
257  _synctex_error("! _synctex_copy_with_quoting_last_path_component: Copy problem");
258  free(dest);
259  dest = NULL;/* Don't forget to reinitialize. */
260  return -2;
261  }
262  memmove(dpc+1,dpc,strlen(dpc)+1); /* Also move the null terminating character. */
263  dpc[0]='"';
264  dpc[strlen(dpc)+1]='\0';/* Consistency test */
265  dpc[strlen(dpc)]='"';
266  return 0; /* Success. */
267  }
268  return -1; /* Memory allocation error. */
269  }
270  _synctex_error("! _synctex_copy_with_quoting_last_path_component: Internal inconsistency");
271  return -3;
272  }
273  return 0; /* Success. */
274  }
275  return 0; /* No last path component. */
276 # undef dest
277  }
278  return 1; /* Bad parameter, this value is subject to changes. */
279 }
280 
281 /* The client is responsible of the management of the returned string, if any. */
282 char * _synctex_merge_strings(const char * first,...);
283 
284 char * _synctex_merge_strings(const char * first,...) {
285  va_list arg;
286  size_t size = 0;
287  const char * temp;
288  /* First retrieve the size necessary to store the merged string */
289  va_start (arg, first);
290  temp = first;
291  do {
292  size_t len = strlen(temp);
293  if(UINT_MAX-len<size) {
294  _synctex_error("! _synctex_merge_strings: Capacity exceeded.");
295  return NULL;
296  }
297  size+=len;
298  } while( (temp = va_arg(arg, const char *)) != NULL);
299  va_end(arg);
300  if(size>0) {
301  char * result = NULL;
302  ++size;
303  /* Create the memory storage */
304  if(NULL!=(result = (char *)malloc(size))) {
305  char * dest = result;
306  va_start (arg, first);
307  temp = first;
308  do {
309  if((size = strlen(temp))>0) {
310  /* There is something to merge */
311  if(dest != strncpy(dest,temp,size)) {
312  _synctex_error("! _synctex_merge_strings: Copy problem");
313  free(result);
314  result = NULL;
315  return NULL;
316  }
317  dest += size;
318  }
319  } while( (temp = va_arg(arg, const char *)) != NULL);
320  va_end(arg);
321  dest[0]='\0';/* Terminate the merged string */
322  return result;
323  }
324  _synctex_error("! _synctex_merge_strings: Memory problem");
325  return NULL;
326  }
327  return NULL;
328 }
329 
330 /* The purpose of _synctex_get_name is to find the name of the synctex file.
331  * There is a list of possible filenames from which we return the most recent one and try to remove all the others.
332  * With two runs of pdftex or xetex we are sure the the synctex file is really the most appropriate.
333  */
334 int _synctex_get_name(const char * output, const char * build_directory, char ** synctex_name_ref, synctex_io_mode_t * io_mode_ref)
335 {
336  if(output && synctex_name_ref && io_mode_ref) {
337  /* If output is already absolute, we just have to manage the quotes and the compress mode */
338  size_t size = 0;
339  char * synctex_name = NULL;
340  synctex_io_mode_t io_mode = *io_mode_ref;
341  const char * base_name = _synctex_last_path_component(output); /* do not free, output is the owner. base name of output*/
342  /* Do we have a real base name ? */
343  if(strlen(base_name)>0) {
344  /* Yes, we do. */
345  const char * temp = NULL;
346  char * core_name = NULL; /* base name of output without path extension. */
347  char * dir_name = NULL; /* dir name of output */
348  char * quoted_core_name = NULL;
349  char * basic_name = NULL;
350  char * gz_name = NULL;
351  char * quoted_name = NULL;
352  char * quoted_gz_name = NULL;
353  char * build_name = NULL;
354  char * build_gz_name = NULL;
355  char * build_quoted_name = NULL;
356  char * build_quoted_gz_name = NULL;
357  struct stat buf;
358  time_t the_time = 0;
359  /* Create core_name: let temp point to the dot before the path extension of base_name;
360  * We start form the \0 terminating character and scan the string upward until we find a dot.
361  * The leading dot is not accepted. */
362  if((temp = strrchr(base_name,'.')) && (size = temp - base_name)>0) {
363  /* There is a dot and it is not at the leading position */
364  if(NULL == (core_name = (char *)malloc(size+1))) {
365  _synctex_error("! _synctex_get_name: Memory problem 1");
366  return -1;
367  }
368  if(core_name != strncpy(core_name,base_name,size)) {
369  _synctex_error("! _synctex_get_name: Copy problem 1");
370  free(core_name);
371  dir_name = NULL;
372  return -2;
373  }
374  core_name[size] = '\0';
375  } else {
376  /* There is no path extension,
377  * Just make a copy of base_name */
378  core_name = _synctex_merge_strings(base_name);
379  }
380  /* core_name is properly set up, owned by "self". */
381  /* creating dir_name. */
382  size = strlen(output)-strlen(base_name);
383  if(size>0) {
384  /* output contains more than one path component */
385  if(NULL == (dir_name = (char *)malloc(size+1))) {
386  _synctex_error("! _synctex_get_name: Memory problem");
387  free(core_name);
388  dir_name = NULL;
389  return -1;
390  }
391  if(dir_name != strncpy(dir_name,output,size)) {
392  _synctex_error("! _synctex_get_name: Copy problem");
393  free(dir_name);
394  dir_name = NULL;
395  free(core_name);
396  dir_name = NULL;
397  return -2;
398  }
399  dir_name[size] = '\0';
400  }
401  /* dir_name is properly set up. It ends with a path separator, if non void. */
402  /* creating quoted_core_name. */
403  if(strchr(core_name,' ')) {
404  quoted_core_name = _synctex_merge_strings("\"",core_name,"\"");
405  }
406  /* quoted_core_name is properly set up. */
407  if(dir_name &&strlen(dir_name)>0) {
408  basic_name = _synctex_merge_strings(dir_name,core_name,synctex_suffix,NULL);
409  if(quoted_core_name && strlen(quoted_core_name)>0) {
410  quoted_name = _synctex_merge_strings(dir_name,quoted_core_name,synctex_suffix,NULL);
411  }
412  } else {
413  basic_name = _synctex_merge_strings(core_name,synctex_suffix,NULL);
414  if(quoted_core_name && strlen(quoted_core_name)>0) {
415  quoted_name = _synctex_merge_strings(quoted_core_name,synctex_suffix,NULL);
416  }
417  }
418  if(!_synctex_path_is_absolute(output) && build_directory && (size = strlen(build_directory))) {
419  temp = build_directory + size - 1;
420  if(_synctex_path_is_absolute(temp)) {
421  build_name = _synctex_merge_strings(build_directory,basic_name,NULL);
422  if(quoted_core_name && strlen(quoted_core_name)>0) {
423  build_quoted_name = _synctex_merge_strings(build_directory,quoted_name,NULL);
424  }
425  } else {
426  build_name = _synctex_merge_strings(build_directory,"/",basic_name,NULL);
427  if(quoted_core_name && strlen(quoted_core_name)>0) {
428  build_quoted_name = _synctex_merge_strings(build_directory,"/",quoted_name,NULL);
429  }
430  }
431  }
432  if(basic_name) {
433  gz_name = _synctex_merge_strings(basic_name,synctex_suffix_gz,NULL);
434  }
435  if(quoted_name) {
436  quoted_gz_name = _synctex_merge_strings(quoted_name,synctex_suffix_gz,NULL);
437  }
438  if(build_name) {
439  build_gz_name = _synctex_merge_strings(build_name,synctex_suffix_gz,NULL);
440  }
441  if(build_quoted_name) {
442  build_quoted_gz_name = _synctex_merge_strings(build_quoted_name,synctex_suffix_gz,NULL);
443  }
444  /* All the others names are properly set up... */
445  /* retain the most recently modified file */
446 # define TEST(FILENAME,COMPRESS_MODE) \
447  if(FILENAME) {\
448  if (stat(FILENAME, &buf)) { \
449  free(FILENAME);\
450  FILENAME = NULL;\
451  } else if (buf.st_mtime>the_time) { \
452  the_time=buf.st_mtime; \
453  synctex_name = FILENAME; \
454  if (COMPRESS_MODE) { \
455  io_mode |= synctex_io_gz_mask; \
456  } else { \
457  io_mode &= ~synctex_io_gz_mask; \
458  } \
459  } \
460  }
461  TEST(basic_name,synctex_DONT_COMPRESS);
462  TEST(gz_name,synctex_COMPRESS);
463  TEST(quoted_name,synctex_DONT_COMPRESS);
464  TEST(quoted_gz_name,synctex_COMPRESS);
465  TEST(build_name,synctex_DONT_COMPRESS);
466  TEST(build_gz_name,synctex_COMPRESS);
467  TEST(build_quoted_name,synctex_DONT_COMPRESS);
468  TEST(build_quoted_gz_name,synctex_COMPRESS);
469 # undef TEST
470  /* Free all the intermediate filenames, except the one that will be used as returned value. */
471 # define CLEAN_AND_REMOVE(FILENAME) \
472  if(FILENAME && (FILENAME!=synctex_name)) {\
473  remove(FILENAME);\
474  printf("synctex tool info: %s removed\n",FILENAME);\
475  free(FILENAME);\
476  FILENAME = NULL;\
477  }
478  CLEAN_AND_REMOVE(basic_name);
479  CLEAN_AND_REMOVE(gz_name);
480  CLEAN_AND_REMOVE(quoted_name);
481  CLEAN_AND_REMOVE(quoted_gz_name);
482  CLEAN_AND_REMOVE(build_name);
483  CLEAN_AND_REMOVE(build_gz_name);
484  CLEAN_AND_REMOVE(build_quoted_name);
485  CLEAN_AND_REMOVE(build_quoted_gz_name);
486 # undef CLEAN_AND_REMOVE
487  /* set up the returned values */
488  * synctex_name_ref = synctex_name;
489  * io_mode_ref = io_mode;
490  return 0;
491  }
492  return -1;/* bad argument */
493  }
494  return -2;
495 }
496 
498  static const char * synctex_io_modes[4] = {"r","rb","a","ab"};
499  unsigned index = ((io_mode & synctex_io_gz_mask)?1:0) + ((io_mode & synctex_io_append_mask)?2:0);// bug pointed out by Jose Alliste
500  return synctex_io_modes[index];
501 }