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 #include <shlwapi.h> /* Use shlwapi.lib */
71 #endif
72
_synctex_malloc(size_t size)73 void *_synctex_malloc(size_t size) {
74 void * ptr = malloc(size);
75 if(ptr) {
76 /* There used to be a switch to use bzero because it is more secure. JL */
77 memset(ptr,0, size);
78 }
79 return (void *)ptr;
80 }
81
_synctex_error(const char * reason,...)82 int _synctex_error(const char * reason,...) {
83 va_list arg;
84 int result;
85 va_start (arg, reason);
86 # ifdef SYNCTEX_RECENT_WINDOWS
87 {/* This code is contributed by William Blum.
88 As it does not work on some older computers,
89 the _WIN32 conditional here is replaced with a SYNCTEX_RECENT_WINDOWS one.
90 According to http://msdn.microsoft.com/en-us/library/aa363362(VS.85).aspx
91 Minimum supported client Windows 2000 Professional
92 Minimum supported server Windows 2000 Server
93 People running Windows 2K standard edition will not have OutputDebugStringA.
94 JL.*/
95 char *buff;
96 size_t len;
97 OutputDebugStringA("SyncTeX ERROR: ");
98 len = _vscprintf(reason, arg) + 1;
99 buff = (char*)malloc( len * sizeof(char) );
100 result = vsprintf(buff, reason, arg) +strlen("SyncTeX ERROR: ");
101 OutputDebugStringA(buff);
102 OutputDebugStringA("\n");
103 free(buff);
104 }
105 # else
106 result = fprintf(stderr,"SyncTeX ERROR: ");
107 result += vfprintf(stderr, reason, arg);
108 result += fprintf(stderr,"\n");
109 # endif
110 va_end (arg);
111 return result;
112 }
113
114 /* strip the last extension of the given string, this string is modified! */
_synctex_strip_last_path_extension(char * string)115 void _synctex_strip_last_path_extension(char * string) {
116 if(NULL != string){
117 char * last_component = NULL;
118 char * last_extension = NULL;
119 # if defined(SYNCTEX_WINDOWS)
120 last_component = PathFindFileName(string);
121 last_extension = PathFindExtension(string);
122 if(last_extension == NULL)return;
123 if(last_component == NULL)last_component = string;
124 if(last_extension>last_component){/* filter out paths like "my/dir/.hidden" */
125 last_extension[0] = '\0';
126 }
127 # else
128 char * next = NULL;
129 /* first we find the last path component */
130 if(NULL == (last_component = strstr(string,"/"))){
131 last_component = string;
132 } else {
133 ++last_component;
134 while((next = strstr(last_component,"/"))){
135 last_component = next+1;
136 }
137 }
138 # if defined(SYNCTEX_OS2)
139 /* On OS2, the '\' is also a path separator. */
140 while((next = strstr(last_component,"\\"))){
141 last_component = next+1;
142 }
143 # endif /* SYNCTEX_OS2 */
144 /* then we find the last path extension */
145 if((last_extension = strstr(last_component,"."))){
146 ++last_extension;
147 while((next = strstr(last_extension,"."))){
148 last_extension = next+1;
149 }
150 --last_extension;/* back to the "." */
151 if(last_extension>last_component){/* filter out paths like ....my/dir/.hidden"*/
152 last_extension[0] = '\0';
153 }
154 }
155 # endif /* SYNCTEX_WINDOWS */
156 }
157 }
158
synctex_ignore_leading_dot_slash_in_path(const char ** name_ref)159 synctex_bool_t synctex_ignore_leading_dot_slash_in_path(const char ** name_ref)
160 {
161 if (SYNCTEX_IS_DOT((*name_ref)[0]) && SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[1])) {
162 do {
163 (*name_ref) += 2;
164 while (SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[0])) {
165 ++(*name_ref);
166 }
167 } while(SYNCTEX_IS_DOT((*name_ref)[0]) && SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[1]));
168 return synctex_YES;
169 }
170 return synctex_NO;
171 }
172
173 /* The base name is necessary to deal with the 2011 file naming convention...
174 * path is a '\0' terminated string
175 * The return value is the trailing part of the argument,
176 * just following the first occurrence of the regexp pattern "[^|/|\].[\|/]+".*/
_synctex_base_name(const char * path)177 const char * _synctex_base_name(const char *path) {
178 const char * ptr = path;
179 do {
180 if (synctex_ignore_leading_dot_slash_in_path(&ptr)) {
181 return ptr;
182 }
183 do {
184 if (!*(++ptr)) {
185 return path;
186 }
187 } while (!SYNCTEX_IS_PATH_SEPARATOR(*ptr));
188 } while (*(++ptr));
189 return path;
190 }
191
192 /* Compare two file names, windows is sometimes case insensitive... */
_synctex_is_equivalent_file_name(const char * lhs,const char * rhs)193 synctex_bool_t _synctex_is_equivalent_file_name(const char *lhs, const char *rhs) {
194 /* Remove the leading regex '(\./+)*' in both rhs and lhs */
195 synctex_ignore_leading_dot_slash_in_path(&lhs);
196 synctex_ignore_leading_dot_slash_in_path(&rhs);
197 next_character:
198 if (SYNCTEX_IS_PATH_SEPARATOR(*lhs)) {/* lhs points to a path separator */
199 if (!SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/* but not rhs */
200 return synctex_NO;
201 }
202 ++lhs;
203 ++rhs;
204 synctex_ignore_leading_dot_slash_in_path(&lhs);
205 synctex_ignore_leading_dot_slash_in_path(&rhs);
206 goto next_character;
207 } else if (SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/* rhs points to a path separator but not lhs */
208 return synctex_NO;
209 } else if (SYNCTEX_ARE_PATH_CHARACTERS_EQUAL(*lhs,*rhs)){/* uppercase do not match */
210 return synctex_NO;
211 } else if (!*lhs) {/* lhs is at the end of the string */
212 return *rhs ? synctex_NO : synctex_YES;
213 } else if(!*rhs) {/* rhs is at the end of the string but not lhs */
214 return synctex_NO;
215 }
216 ++lhs;
217 ++rhs;
218 goto next_character;
219 }
220
_synctex_path_is_absolute(const char * name)221 synctex_bool_t _synctex_path_is_absolute(const char * name) {
222 if(!strlen(name)) {
223 return synctex_NO;
224 }
225 # if defined(SYNCTEX_WINDOWS) || defined(SYNCTEX_OS2)
226 if(strlen(name)>2) {
227 return (name[1]==':' && SYNCTEX_IS_PATH_SEPARATOR(name[2]))?synctex_YES:synctex_NO;
228 }
229 return synctex_NO;
230 # else
231 return SYNCTEX_IS_PATH_SEPARATOR(name[0])?synctex_YES:synctex_NO;
232 # endif
233 }
234
235 /* We do not take care of UTF-8 */
_synctex_last_path_component(const char * name)236 const char * _synctex_last_path_component(const char * name) {
237 const char * c = name+strlen(name);
238 if(c>name) {
239 if(!SYNCTEX_IS_PATH_SEPARATOR(*c)) {
240 do {
241 --c;
242 if(SYNCTEX_IS_PATH_SEPARATOR(*c)) {
243 return c+1;
244 }
245 } while(c>name);
246 }
247 return c;/* the last path component is the void string*/
248 }
249 return c;
250 }
251
_synctex_copy_with_quoting_last_path_component(const char * src,char ** dest_ref,size_t size)252 int _synctex_copy_with_quoting_last_path_component(const char * src, char ** dest_ref, size_t size) {
253 const char * lpc;
254 if(src && dest_ref) {
255 # define dest (*dest_ref)
256 dest = NULL; /* Default behavior: no change and sucess. */
257 lpc = _synctex_last_path_component(src);
258 if(strlen(lpc)) {
259 if(strchr(lpc,' ') && lpc[0]!='"' && lpc[strlen(lpc)-1]!='"') {
260 /* We are in the situation where adding the quotes is allowed. */
261 /* Time to add the quotes. */
262 /* Consistency test: we must have dest+size>dest+strlen(dest)+2
263 * or equivalently: strlen(dest)+2<size (see below) */
264 if(strlen(src)<size) {
265 if((dest = (char *)malloc(size+2))) {
266 char * dpc = dest + (lpc-src); /* dpc is the last path component of dest. */
267 if(dest != strncpy(dest,src,size)) {
268 _synctex_error("! _synctex_copy_with_quoting_last_path_component: Copy problem");
269 free(dest);
270 dest = NULL;/* Don't forget to reinitialize. */
271 return -2;
272 }
273 memmove(dpc+1,dpc,strlen(dpc)+1); /* Also move the null terminating character. */
274 dpc[0]='"';
275 dpc[strlen(dpc)+1]='\0';/* Consistency test */
276 dpc[strlen(dpc)]='"';
277 return 0; /* Success. */
278 }
279 return -1; /* Memory allocation error. */
280 }
281 _synctex_error("! _synctex_copy_with_quoting_last_path_component: Internal inconsistency");
282 return -3;
283 }
284 return 0; /* Success. */
285 }
286 return 0; /* No last path component. */
287 # undef dest
288 }
289 return 1; /* Bad parameter, this value is subject to changes. */
290 }
291
292 /* The client is responsible of the management of the returned string, if any. */
293 char * _synctex_merge_strings(const char * first,...);
294
_synctex_merge_strings(const char * first,...)295 char * _synctex_merge_strings(const char * first,...) {
296 va_list arg;
297 size_t size = 0;
298 const char * temp;
299 /* First retrieve the size necessary to store the merged string */
300 va_start (arg, first);
301 temp = first;
302 do {
303 size_t len = strlen(temp);
304 if(UINT_MAX-len<size) {
305 _synctex_error("! _synctex_merge_strings: Capacity exceeded.");
306 return NULL;
307 }
308 size+=len;
309 } while( (temp = va_arg(arg, const char *)) != NULL);
310 va_end(arg);
311 if(size>0) {
312 char * result = NULL;
313 ++size;
314 /* Create the memory storage */
315 if(NULL!=(result = (char *)malloc(size))) {
316 char * dest = result;
317 va_start (arg, first);
318 temp = first;
319 do {
320 if((size = strlen(temp))>0) {
321 /* There is something to merge */
322 if(dest != strncpy(dest,temp,size)) {
323 _synctex_error("! _synctex_merge_strings: Copy problem");
324 free(result);
325 result = NULL;
326 return NULL;
327 }
328 dest += size;
329 }
330 } while( (temp = va_arg(arg, const char *)) != NULL);
331 va_end(arg);
332 dest[0]='\0';/* Terminate the merged string */
333 return result;
334 }
335 _synctex_error("! _synctex_merge_strings: Memory problem");
336 return NULL;
337 }
338 return NULL;
339 }
340
341 /* The purpose of _synctex_get_name is to find the name of the synctex file.
342 * There is a list of possible filenames from which we return the most recent one and try to remove all the others.
343 * With two runs of pdftex or xetex we are sure the the synctex file is really the most appropriate.
344 */
_synctex_get_name(const char * output,const char * build_directory,char ** synctex_name_ref,synctex_io_mode_t * io_mode_ref)345 int _synctex_get_name(const char * output, const char * build_directory, char ** synctex_name_ref, synctex_io_mode_t * io_mode_ref)
346 {
347 if(output && synctex_name_ref && io_mode_ref) {
348 /* If output is already absolute, we just have to manage the quotes and the compress mode */
349 size_t size = 0;
350 char * synctex_name = NULL;
351 synctex_io_mode_t io_mode = *io_mode_ref;
352 const char * base_name = _synctex_last_path_component(output); /* do not free, output is the owner. base name of output*/
353 /* Do we have a real base name ? */
354 if(strlen(base_name)>0) {
355 /* Yes, we do. */
356 const char * temp = NULL;
357 char * core_name = NULL; /* base name of output without path extension. */
358 char * dir_name = NULL; /* dir name of output */
359 char * quoted_core_name = NULL;
360 char * basic_name = NULL;
361 char * gz_name = NULL;
362 char * quoted_name = NULL;
363 char * quoted_gz_name = NULL;
364 char * build_name = NULL;
365 char * build_gz_name = NULL;
366 char * build_quoted_name = NULL;
367 char * build_quoted_gz_name = NULL;
368 struct stat buf;
369 time_t the_time = 0;
370 /* Create core_name: let temp point to the dot before the path extension of base_name;
371 * We start form the \0 terminating character and scan the string upward until we find a dot.
372 * The leading dot is not accepted. */
373 if((temp = strrchr(base_name,'.')) && (size = temp - base_name)>0) {
374 /* There is a dot and it is not at the leading position */
375 if(NULL == (core_name = (char *)malloc(size+1))) {
376 _synctex_error("! _synctex_get_name: Memory problem 1");
377 return -1;
378 }
379 if(core_name != strncpy(core_name,base_name,size)) {
380 _synctex_error("! _synctex_get_name: Copy problem 1");
381 free(core_name);
382 dir_name = NULL;
383 return -2;
384 }
385 core_name[size] = '\0';
386 } else {
387 /* There is no path extension,
388 * Just make a copy of base_name */
389 core_name = _synctex_merge_strings(base_name);
390 }
391 /* core_name is properly set up, owned by "self". */
392 /* creating dir_name. */
393 size = strlen(output)-strlen(base_name);
394 if(size>0) {
395 /* output contains more than one path component */
396 if(NULL == (dir_name = (char *)malloc(size+1))) {
397 _synctex_error("! _synctex_get_name: Memory problem");
398 free(core_name);
399 dir_name = NULL;
400 return -1;
401 }
402 if(dir_name != strncpy(dir_name,output,size)) {
403 _synctex_error("! _synctex_get_name: Copy problem");
404 free(dir_name);
405 dir_name = NULL;
406 free(core_name);
407 dir_name = NULL;
408 return -2;
409 }
410 dir_name[size] = '\0';
411 }
412 /* dir_name is properly set up. It ends with a path separator, if non void. */
413 /* creating quoted_core_name. */
414 if(strchr(core_name,' ')) {
415 quoted_core_name = _synctex_merge_strings("\"",core_name,"\"");
416 }
417 /* quoted_core_name is properly set up. */
418 if(dir_name &&strlen(dir_name)>0) {
419 basic_name = _synctex_merge_strings(dir_name,core_name,synctex_suffix,NULL);
420 if(quoted_core_name && strlen(quoted_core_name)>0) {
421 quoted_name = _synctex_merge_strings(dir_name,quoted_core_name,synctex_suffix,NULL);
422 }
423 } else {
424 basic_name = _synctex_merge_strings(core_name,synctex_suffix,NULL);
425 if(quoted_core_name && strlen(quoted_core_name)>0) {
426 quoted_name = _synctex_merge_strings(quoted_core_name,synctex_suffix,NULL);
427 }
428 }
429 if(!_synctex_path_is_absolute(output) && build_directory && (size = strlen(build_directory))) {
430 temp = build_directory + size - 1;
431 if(_synctex_path_is_absolute(temp)) {
432 build_name = _synctex_merge_strings(build_directory,basic_name,NULL);
433 if(quoted_core_name && strlen(quoted_core_name)>0) {
434 build_quoted_name = _synctex_merge_strings(build_directory,quoted_name,NULL);
435 }
436 } else {
437 build_name = _synctex_merge_strings(build_directory,"/",basic_name,NULL);
438 if(quoted_core_name && strlen(quoted_core_name)>0) {
439 build_quoted_name = _synctex_merge_strings(build_directory,"/",quoted_name,NULL);
440 }
441 }
442 }
443 if(basic_name) {
444 gz_name = _synctex_merge_strings(basic_name,synctex_suffix_gz,NULL);
445 }
446 if(quoted_name) {
447 quoted_gz_name = _synctex_merge_strings(quoted_name,synctex_suffix_gz,NULL);
448 }
449 if(build_name) {
450 build_gz_name = _synctex_merge_strings(build_name,synctex_suffix_gz,NULL);
451 }
452 if(build_quoted_name) {
453 build_quoted_gz_name = _synctex_merge_strings(build_quoted_name,synctex_suffix_gz,NULL);
454 }
455 /* All the others names are properly set up... */
456 /* retain the most recently modified file */
457 # define TEST(FILENAME,COMPRESS_MODE) \
458 if(FILENAME) {\
459 if (stat(FILENAME, &buf)) { \
460 free(FILENAME);\
461 FILENAME = NULL;\
462 } else if (buf.st_mtime>the_time) { \
463 the_time=buf.st_mtime; \
464 synctex_name = FILENAME; \
465 if (COMPRESS_MODE) { \
466 io_mode |= synctex_io_gz_mask; \
467 } else { \
468 io_mode &= ~synctex_io_gz_mask; \
469 } \
470 } \
471 }
472 TEST(basic_name,synctex_DONT_COMPRESS);
473 TEST(gz_name,synctex_COMPRESS);
474 TEST(quoted_name,synctex_DONT_COMPRESS);
475 TEST(quoted_gz_name,synctex_COMPRESS);
476 TEST(build_name,synctex_DONT_COMPRESS);
477 TEST(build_gz_name,synctex_COMPRESS);
478 TEST(build_quoted_name,synctex_DONT_COMPRESS);
479 TEST(build_quoted_gz_name,synctex_COMPRESS);
480 # undef TEST
481 /* Free all the intermediate filenames, except the one that will be used as returned value. */
482 # define CLEAN_AND_REMOVE(FILENAME) \
483 if(FILENAME && (FILENAME!=synctex_name)) {\
484 remove(FILENAME);\
485 printf("synctex tool info: %s removed\n",FILENAME);\
486 free(FILENAME);\
487 FILENAME = NULL;\
488 }
489 CLEAN_AND_REMOVE(basic_name);
490 CLEAN_AND_REMOVE(gz_name);
491 CLEAN_AND_REMOVE(quoted_name);
492 CLEAN_AND_REMOVE(quoted_gz_name);
493 CLEAN_AND_REMOVE(build_name);
494 CLEAN_AND_REMOVE(build_gz_name);
495 CLEAN_AND_REMOVE(build_quoted_name);
496 CLEAN_AND_REMOVE(build_quoted_gz_name);
497 # undef CLEAN_AND_REMOVE
498 /* set up the returned values */
499 * synctex_name_ref = synctex_name;
500 * io_mode_ref = io_mode;
501 return 0;
502 }
503 return -1;/* bad argument */
504 }
505 return -2;
506 }
507
_synctex_get_io_mode_name(synctex_io_mode_t io_mode)508 const char * _synctex_get_io_mode_name(synctex_io_mode_t io_mode) {
509 static const char * synctex_io_modes[4] = {"r","rb","a","ab"};
510 unsigned index = ((io_mode & synctex_io_gz_mask)?1:0) + ((io_mode & synctex_io_append_mask)?2:0);// bug pointed out by Jose Alliste
511 return synctex_io_modes[index];
512 }
513