2 Copyright (c) 2008, 2009, 2010 , 2011 jerome DOT laurens AT u-bourgogne DOT fr
4 This file is part of the SyncTeX package.
6 Latest Revision: Fri Mar 11 07:39:12 UTC 2011
10 See synctex_parser_readme.txt for more details
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
23 The above copyright notice and this permission notice shall be
24 included in all copies or substantial portions of the Software.
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
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.
42 /* In this file, we find all the functions that may depend on the operating system. */
44 #include <synctex_parser_utils.h>
56 #if defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
57 #define SYNCTEX_WINDOWS 1
60 #ifdef _WIN32_WINNT_WINXP
61 #define SYNCTEX_RECENT_WINDOWS 1
64 #ifdef SYNCTEX_WINDOWS
68 void *_synctex_malloc(size_t size) {
69 void * ptr = malloc(size);
71 /* There used to be a switch to use bzero because it is more secure. JL */
77 int _synctex_error(const char * reason,...) {
80 va_start (arg, reason);
81 # ifdef SYNCTEX_RECENT_WINDOWS
82 {/* This code is contributed by William Blum.
83 As it does not work on some older computers,
84 the _WIN32 conditional here is replaced with a SYNCTEX_RECENT_WINDOWS one.
85 According to http://msdn.microsoft.com/en-us/library/aa363362(VS.85).aspx
86 Minimum supported client Windows 2000 Professional
87 Minimum supported server Windows 2000 Server
88 People running Windows 2K standard edition will not have OutputDebugStringA.
92 OutputDebugStringA("SyncTeX ERROR: ");
93 len = _vscprintf(reason, arg) + 1;
94 buff = (char*)malloc( len * sizeof(char) );
95 result = vsprintf(buff, reason, arg) +strlen("SyncTeX ERROR: ");
96 OutputDebugStringA(buff);
97 OutputDebugStringA("\n");
101 result = fprintf(stderr,"SyncTeX ERROR: ");
102 result += vfprintf(stderr, reason, arg);
103 result += fprintf(stderr,"\n");
109 /* strip the last extension of the given string, this string is modified! */
110 void _synctex_strip_last_path_extension(char * string) {
112 char * last_component = NULL;
113 char * last_extension = NULL;
115 /* first we find the last path component */
116 if(NULL == (last_component = strstr(string,"/"))){
117 last_component = string;
120 while((next = strstr(last_component,"/"))){
121 last_component = next+1;
124 # ifdef SYNCTEX_WINDOWS
125 /* On Windows, the '\' is also a path separator. */
126 while((next = strstr(last_component,"\\"))){
127 last_component = next+1;
130 /* then we find the last path extension */
131 if((last_extension = strstr(last_component,"."))){
133 while((next = strstr(last_extension,"."))){
134 last_extension = next+1;
136 --last_extension;/* back to the "." */
137 if(last_extension>last_component){/* filter out paths like ....my/dir/.hidden"*/
138 last_extension[0] = '\0';
144 /* Compare two file names, windows is sometimes case insensitive... */
145 synctex_bool_t _synctex_is_equivalent_file_name(const char *lhs, const char *rhs) {
147 /* On Windows, filename should be compared case insensitive.
148 * The characters '/' and '\' are both valid path separators.
149 * There will be a very serious problem concerning UTF8 because
150 * not all the characters must be toupper...
151 * I would like to have URL's instead of filenames. */
153 if(SYNCTEX_IS_PATH_SEPARATOR(*lhs)) {/* lhs points to a path separator */
154 if(!SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/* but not rhs */
157 } else if(SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/* rhs points to a path separator but not lhs */
159 } else if(toupper(*lhs) != toupper(*rhs)){/* uppercase do not match */
161 } else if (!*lhs) {/* lhs is at the end of the string */
162 return *rhs ? synctex_NO : synctex_YES;
163 } else if(!*rhs) {/* rhs is at the end of the string but not lhs */
170 return 0 == strcmp(lhs,rhs)?synctex_YES:synctex_NO;
174 synctex_bool_t _synctex_path_is_absolute(const char * name) {
180 return (name[1]==':' && SYNCTEX_IS_PATH_SEPARATOR(name[2]))?synctex_YES:synctex_NO;
184 return SYNCTEX_IS_PATH_SEPARATOR(name[0])?synctex_YES:synctex_NO;
188 /* We do not take care of UTF-8 */
189 const char * _synctex_last_path_component(const char * name) {
190 const char * c = name+strlen(name);
192 if(!SYNCTEX_IS_PATH_SEPARATOR(*c)) {
195 if(SYNCTEX_IS_PATH_SEPARATOR(*c)) {
200 return c;/* the last path component is the void string*/
205 int _synctex_copy_with_quoting_last_path_component(const char * src, char ** dest_ref, size_t size) {
207 if(src && dest_ref) {
208 # define dest (*dest_ref)
209 dest = NULL; /* Default behavior: no change and sucess. */
210 lpc = _synctex_last_path_component(src);
212 if(strchr(lpc,' ') && lpc[0]!='"' && lpc[strlen(lpc)-1]!='"') {
213 /* We are in the situation where adding the quotes is allowed. */
214 /* Time to add the quotes. */
215 /* Consistency test: we must have dest+size>dest+strlen(dest)+2
216 * or equivalently: strlen(dest)+2<size (see below) */
217 if(strlen(src)<size) {
218 if((dest = (char *)malloc(size+2))) {
219 char * dpc = dest + (lpc-src); /* dpc is the last path component of dest. */
220 if(dest != strncpy(dest,src,size)) {
221 _synctex_error("! _synctex_copy_with_quoting_last_path_component: Copy problem");
223 dest = NULL;/* Don't forget to reinitialize. */
226 memmove(dpc+1,dpc,strlen(dpc)+1); /* Also move the null terminating character. */
228 dpc[strlen(dpc)+1]='\0';/* Consistency test */
229 dpc[strlen(dpc)]='"';
230 return 0; /* Success. */
232 return -1; /* Memory allocation error. */
234 _synctex_error("! _synctex_copy_with_quoting_last_path_component: Internal inconsistency");
237 return 0; /* Success. */
239 return 0; /* No last path component. */
242 return 1; /* Bad parameter, this value is subject to changes. */
245 /* The client is responsible of the management of the returned string, if any. */
246 char * _synctex_merge_strings(const char * first,...);
248 char * _synctex_merge_strings(const char * first,...) {
252 /* First retrieve the size necessary to store the merged string */
253 va_start (arg, first);
256 size_t len = strlen(temp);
257 if(UINT_MAX-len<size) {
258 _synctex_error("! _synctex_merge_strings: Capacity exceeded.");
262 } while( (temp = va_arg(arg, const char *)) != NULL);
265 char * result = NULL;
267 /* Create the memory storage */
268 if(NULL!=(result = (char *)malloc(size))) {
269 char * dest = result;
270 va_start (arg, first);
273 if((size = strlen(temp))>0) {
274 /* There is something to merge */
275 if(dest != strncpy(dest,temp,size)) {
276 _synctex_error("! _synctex_merge_strings: Copy problem");
283 } while( (temp = va_arg(arg, const char *)) != NULL);
285 dest[0]='\0';/* Terminate the merged string */
288 _synctex_error("! _synctex_merge_strings: Memory problem");
294 /* The purpose of _synctex_get_name is to find the name of the synctex file.
295 * There is a list of possible filenames from which we return the most recent one and try to remove all the others.
296 * With two runs of pdftex or xetex we are sure the the synctex file is really the most appropriate.
298 int _synctex_get_name(const char * output, const char * build_directory, char ** synctex_name_ref, synctex_io_mode_t * io_mode_ref)
300 if(output && synctex_name_ref && io_mode_ref) {
301 /* If output is already absolute, we just have to manage the quotes and the compress mode */
303 char * synctex_name = NULL;
304 synctex_io_mode_t io_mode = *io_mode_ref;
305 const char * base_name = _synctex_last_path_component(output); /* do not free, output is the owner. base name of output*/
306 /* Do we have a real base name ? */
307 if(strlen(base_name)>0) {
309 const char * temp = NULL;
310 char * core_name = NULL; /* base name of output without path extension. */
311 char * dir_name = NULL; /* dir name of output */
312 char * quoted_core_name = NULL;
313 char * basic_name = NULL;
314 char * gz_name = NULL;
315 char * quoted_name = NULL;
316 char * quoted_gz_name = NULL;
317 char * build_name = NULL;
318 char * build_gz_name = NULL;
319 char * build_quoted_name = NULL;
320 char * build_quoted_gz_name = NULL;
323 /* Create core_name: let temp point to the dot before the path extension of base_name;
324 * We start form the \0 terminating character and scan the string upward until we find a dot.
325 * The leading dot is not accepted. */
326 if((temp = strrchr(base_name,'.')) && (size = temp - base_name)>0) {
327 /* There is a dot and it is not at the leading position */
328 if(NULL == (core_name = (char *)malloc(size+1))) {
329 _synctex_error("! _synctex_get_name: Memory problem 1");
332 if(core_name != strncpy(core_name,base_name,size)) {
333 _synctex_error("! _synctex_get_name: Copy problem 1");
338 core_name[size] = '\0';
340 /* There is no path extension,
341 * Just make a copy of base_name */
342 core_name = _synctex_merge_strings(base_name);
344 /* core_name is properly set up, owned by "self". */
345 /* creating dir_name. */
346 size = strlen(output)-strlen(base_name);
348 /* output contains more than one path component */
349 if(NULL == (dir_name = (char *)malloc(size+1))) {
350 _synctex_error("! _synctex_get_name: Memory problem");
355 if(dir_name != strncpy(dir_name,output,size)) {
356 _synctex_error("! _synctex_get_name: Copy problem");
363 dir_name[size] = '\0';
365 /* dir_name is properly set up. It ends with a path separator, if non void. */
366 /* creating quoted_core_name. */
367 if(strchr(core_name,' ')) {
368 quoted_core_name = _synctex_merge_strings("\"",core_name,"\"");
370 /* quoted_core_name is properly set up. */
371 if(dir_name &&strlen(dir_name)>0) {
372 basic_name = _synctex_merge_strings(dir_name,core_name,synctex_suffix,NULL);
373 if(quoted_core_name && strlen(quoted_core_name)>0) {
374 quoted_name = _synctex_merge_strings(dir_name,quoted_core_name,synctex_suffix,NULL);
377 basic_name = _synctex_merge_strings(core_name,synctex_suffix,NULL);
378 if(quoted_core_name && strlen(quoted_core_name)>0) {
379 quoted_name = _synctex_merge_strings(quoted_core_name,synctex_suffix,NULL);
382 if(!_synctex_path_is_absolute(output) && build_directory && (size = strlen(build_directory))) {
383 temp = build_directory + size - 1;
384 if(_synctex_path_is_absolute(temp)) {
385 build_name = _synctex_merge_strings(build_directory,basic_name,NULL);
386 if(quoted_core_name && strlen(quoted_core_name)>0) {
387 build_quoted_name = _synctex_merge_strings(build_directory,quoted_name,NULL);
390 build_name = _synctex_merge_strings(build_directory,"/",basic_name,NULL);
391 if(quoted_core_name && strlen(quoted_core_name)>0) {
392 build_quoted_name = _synctex_merge_strings(build_directory,"/",quoted_name,NULL);
397 gz_name = _synctex_merge_strings(basic_name,synctex_suffix_gz,NULL);
400 quoted_gz_name = _synctex_merge_strings(quoted_name,synctex_suffix_gz,NULL);
403 build_gz_name = _synctex_merge_strings(build_name,synctex_suffix_gz,NULL);
405 if(build_quoted_name) {
406 build_quoted_gz_name = _synctex_merge_strings(build_quoted_name,synctex_suffix_gz,NULL);
408 /* All the others names are properly set up... */
409 /* retain the most recently modified file */
410 # define TEST(FILENAME,COMPRESS_MODE) \
412 if (stat(FILENAME, &buf)) { \
415 } else if (buf.st_mtime>the_time) { \
416 the_time=buf.st_mtime; \
417 synctex_name = FILENAME; \
418 if (COMPRESS_MODE) { \
419 io_mode |= synctex_io_gz_mask; \
421 io_mode &= ~synctex_io_gz_mask; \
425 TEST(basic_name,synctex_DONT_COMPRESS);
426 TEST(gz_name,synctex_COMPRESS);
427 TEST(quoted_name,synctex_DONT_COMPRESS);
428 TEST(quoted_gz_name,synctex_COMPRESS);
429 TEST(build_name,synctex_DONT_COMPRESS);
430 TEST(build_gz_name,synctex_COMPRESS);
431 TEST(build_quoted_name,synctex_DONT_COMPRESS);
432 TEST(build_quoted_gz_name,synctex_COMPRESS);
434 /* Free all the intermediate filenames, except the one that will be used as returned value. */
435 # define CLEAN_AND_REMOVE(FILENAME) \
436 if(FILENAME && (FILENAME!=synctex_name)) {\
438 printf("synctex tool info: %s removed\n",FILENAME);\
442 CLEAN_AND_REMOVE(basic_name);
443 CLEAN_AND_REMOVE(gz_name);
444 CLEAN_AND_REMOVE(quoted_name);
445 CLEAN_AND_REMOVE(quoted_gz_name);
446 CLEAN_AND_REMOVE(build_name);
447 CLEAN_AND_REMOVE(build_gz_name);
448 CLEAN_AND_REMOVE(build_quoted_name);
449 CLEAN_AND_REMOVE(build_quoted_gz_name);
450 # undef CLEAN_AND_REMOVE
451 /* set up the returned values */
452 * synctex_name_ref = synctex_name;
453 * io_mode_ref = io_mode;
456 return -1;/* bad argument */
461 const char * _synctex_get_io_mode_name(synctex_io_mode_t io_mode) {
462 static const char * synctex_io_modes[4] = {"r","rb","a","ab"};
463 unsigned index = (io_mode & synctex_io_gz_mask) + 2 * (io_mode & synctex_io_append_mask);
464 return synctex_io_modes[index];