2 Copyright (c) 2008, 2009 jerome DOT laurens AT u-bourgogne DOT fr
4 This file is part of the SyncTeX package.
6 Latest Revision: Wed Nov 4 11:52:35 UTC 2009
9 See synctex_parser_readme.txt for more details
13 Permission is hereby granted, free of charge, to any person
14 obtaining a copy of this software and associated documentation
15 files (the "Software"), to deal in the Software without
16 restriction, including without limitation the rights to use,
17 copy, modify, merge, publish, distribute, sublicense, and/or sell
18 copies of the Software, and to permit persons to whom the
19 Software is furnished to do so, subject to the following
22 The above copyright notice and this permission notice shall be
23 included in all copies or substantial portions of the Software.
25 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
27 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
29 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
30 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
31 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
32 OTHER DEALINGS IN THE SOFTWARE
34 Except as contained in this notice, the name of the copyright holder
35 shall not be used in advertising or otherwise to promote the sale,
36 use or other dealings in this Software without prior written
37 authorization from the copyright holder.
41 /* In this file, we find all the functions that may depend on the operating system. */
43 #include <synctex_parser_utils.h>
55 #if defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
56 #define SYNCTEX_WINDOWS 1
59 #ifdef _WIN32_WINNT_WINXP
60 #define SYNCTEX_RECENT_WINDOWS 1
63 #ifdef SYNCTEX_WINDOWS
67 void *_synctex_malloc(size_t size) {
68 void * ptr = malloc(size);
70 /* There used to be a switch to use bzero because it is more secure. JL */
76 int _synctex_error(const char * reason,...) {
79 va_start (arg, reason);
80 # ifdef SYNCTEX_RECENT_WINDOWS
81 {/* This code is contributed by William Blum.
82 As it does not work on some older computers,
83 the _WIN32 conditional here is replaced with a SYNCTEX_RECENT_WINDOWS one.
84 According to http://msdn.microsoft.com/en-us/library/aa363362(VS.85).aspx
85 Minimum supported client Windows 2000 Professional
86 Minimum supported server Windows 2000 Server
87 People running Windows 2K standard edition will not have OutputDebugStringA.
91 OutputDebugStringA("SyncTeX ERROR: ");
92 len = _vscprintf(reason, arg) + 1;
93 buff = (char*)malloc( len * sizeof(char) );
94 result = vsprintf(buff, reason, arg) +strlen("SyncTeX ERROR: ");
95 OutputDebugStringA(buff);
96 OutputDebugStringA("\n");
100 result = fprintf(stderr,"SyncTeX ERROR: ");
101 result += vfprintf(stderr, reason, arg);
102 result += fprintf(stderr,"\n");
108 /* strip the last extension of the given string, this string is modified! */
109 void _synctex_strip_last_path_extension(char * string) {
111 char * last_component = NULL;
112 char * last_extension = NULL;
114 /* first we find the last path component */
115 if(NULL == (last_component = strstr(string,"/"))){
116 last_component = string;
119 while((next = strstr(last_component,"/"))){
120 last_component = next+1;
123 # ifdef SYNCTEX_WINDOWS
124 /* On Windows, the '\' is also a path separator. */
125 while((next = strstr(last_component,"\\"))){
126 last_component = next+1;
129 /* then we find the last path extension */
130 if((last_extension = strstr(last_component,"."))){
132 while((next = strstr(last_extension,"."))){
133 last_extension = next+1;
135 --last_extension;/* back to the "." */
136 if(last_extension>last_component){/* filter out paths like ....my/dir/.hidden"*/
137 last_extension[0] = '\0';
143 /* Compare two file names, windows is sometimes case insensitive... */
144 synctex_bool_t _synctex_is_equivalent_file_name(const char *lhs, const char *rhs) {
146 /* On Windows, filename should be compared case insensitive.
147 * The characters '/' and '\' are both valid path separators.
148 * There will be a very serious problem concerning UTF8 because
149 * not all the characters must be toupper...
150 * I would like to have URL's instead of filenames. */
152 if(SYNCTEX_IS_PATH_SEPARATOR(*lhs)) {/* lhs points to a path separator */
153 if(!SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/* but not rhs */
156 } else if(SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/* rhs points to a path separator but not lhs */
158 } else if(toupper(*lhs) != toupper(*rhs)){/* uppercase do not match */
160 } else if (!*lhs) {/* lhs is at the end of the string */
161 return *rhs ? synctex_NO : synctex_YES;
162 } else if(!*rhs) {/* rhs is at the end of the string but not lhs */
169 return 0 == strcmp(lhs,rhs)?synctex_YES:synctex_NO;
173 synctex_bool_t _synctex_path_is_absolute(const char * name) {
179 return (name[1]==':' && SYNCTEX_IS_PATH_SEPARATOR(name[2]))?synctex_YES:synctex_NO;
183 return SYNCTEX_IS_PATH_SEPARATOR(name[0])?synctex_YES:synctex_NO;
187 /* We do not take care of UTF-8 */
188 const char * _synctex_last_path_component(const char * name) {
189 const char * c = name+strlen(name);
191 if(!SYNCTEX_IS_PATH_SEPARATOR(*c)) {
194 if(SYNCTEX_IS_PATH_SEPARATOR(*c)) {
199 return c;/* the last path component is the void string*/
204 int _synctex_copy_with_quoting_last_path_component(const char * src, char ** dest_ref, size_t size) {
206 if(src && dest_ref) {
207 # define dest (*dest_ref)
208 dest = NULL; /* Default behavior: no change and sucess. */
209 lpc = _synctex_last_path_component(src);
211 if(strchr(lpc,' ') && lpc[0]!='"' && lpc[strlen(lpc)-1]!='"') {
212 /* We are in the situation where adding the quotes is allowed. */
213 /* Time to add the quotes. */
214 /* Consistency test: we must have dest+size>dest+strlen(dest)+2
215 * or equivalently: strlen(dest)+2<size (see below) */
216 if(strlen(src)<size) {
217 if((dest = (char *)malloc(size+2))) {
218 char * dpc = dest + (lpc-src); /* dpc is the last path component of dest. */
219 if(dest != strncpy(dest,src,size)) {
220 _synctex_error("! _synctex_copy_with_quoting_last_path_component: Copy problem");
222 dest = NULL;/* Don't forget to reinitialize. */
225 memmove(dpc+1,dpc,strlen(dpc)+1); /* Also move the null terminating character. */
227 dpc[strlen(dpc)+1]='\0';/* Consistency test */
228 dpc[strlen(dpc)]='"';
229 return 0; /* Success. */
231 return -1; /* Memory allocation error. */
233 _synctex_error("! _synctex_copy_with_quoting_last_path_component: Internal inconsistency");
236 return 0; /* Success. */
238 return 0; /* No last path component. */
241 return 1; /* Bad parameter, this value is subject to changes. */
244 /* The client is responsible of the management of the returned string, if any. */
245 char * _synctex_merge_strings(const char * first,...);
247 char * _synctex_merge_strings(const char * first,...) {
251 /* First retrieve the size necessary to store the merged string */
252 va_start (arg, first);
255 size_t len = strlen(temp);
256 if(UINT_MAX-len<size) {
257 _synctex_error("! _synctex_merge_strings: Capacity exceeded.");
261 } while( (temp = va_arg(arg, const char *)) != NULL);
264 char * result = NULL;
266 /* Create the memory storage */
267 if(NULL!=(result = (char *)malloc(size))) {
268 char * dest = result;
269 va_start (arg, first);
272 if((size = strlen(temp))>0) {
273 /* There is something to merge */
274 if(dest != strncpy(dest,temp,size)) {
275 _synctex_error("! _synctex_merge_strings: Copy problem");
282 } while( (temp = va_arg(arg, const char *)) != NULL);
284 dest[0]='\0';/* Terminate the merged string */
287 _synctex_error("! _synctex_merge_strings: Memory problem");
293 /* The purpose of _synctex_get_name is to find the name of the synctex file.
294 * There is a list of possible filenames from which we return the most recent one and try to remove all the others.
295 * With two runs of pdftex or xetex we are sure the the synctex file is really the most appropriate.
297 int _synctex_get_name(const char * output, const char * build_directory, char ** synctex_name_ref, synctex_compress_mode_t * compress_mode_ref)
299 if(output && synctex_name_ref && compress_mode_ref) {
300 # define synctex_name (*synctex_name_ref)
301 # define compress_mode (*compress_mode_ref)
302 /* If output is already absolute, we just have to manage the quotes and the compress mode */
303 const char * basename = NULL; /* base name of output*/
305 /* Initialize the return values. */
307 compress_mode = synctex_compress_mode_none;
308 basename = _synctex_last_path_component(output); /* do not free, output is the owner. */
309 /* Do we have a real base name ? */
310 if((size = strlen(basename))>0) {
312 const char * temp = NULL;
313 char * corename = NULL; /* base name of output without path extension. */
314 char * dirname = NULL; /* dir name of output */
315 char * quoted_corename = NULL;
318 char * quoted = NULL;
319 char * quoted_gz = NULL;
321 char * build_gz = NULL;
322 char * build_quoted = NULL;
323 char * build_quoted_gz = NULL;
326 /* Create corename: let temp point to the dot before the path extension of basename;
327 * We start form the \0 terminating character and scan the string upward until we find a dot.
328 * The first dot is not accepted. */
329 temp = strrchr(basename,'.');
330 size = temp - basename;
332 /* dot properly found, now create corename */
333 if(NULL == (corename = (char *)malloc(size+1))) {
334 _synctex_error("! _synctex_get_name: Memory problem 1");
337 if(corename != strncpy(corename,basename,size)) {
338 _synctex_error("! _synctex_get_name: Copy problem 1");
343 corename[size] = '\0';
345 /* There is no path extension,
346 * Just make a copy of basename */
347 corename = _synctex_merge_strings(basename);
349 /* corename is properly set up, owned by "self". */
350 /* creating dirname. */
351 size = strlen(output)-strlen(basename);
353 /* output contains more than one path component */
354 if(NULL == (dirname = (char *)malloc(size+1))) {
355 _synctex_error("! _synctex_get_name: Memory problem");
360 if(dirname != strncpy(dirname,output,size)) {
361 _synctex_error("! _synctex_get_name: Copy problem");
368 dirname[size] = '\0';
370 /* dirname is properly set up. It ends with a path separator, if non void. */
371 /* creating quoted_corename. */
372 if(strchr(corename,' ')) {
373 quoted_corename = _synctex_merge_strings("\"",corename,"\"");
375 /* quoted_corename is properly set up. */
376 if(dirname &&strlen(dirname)>0) {
377 none = _synctex_merge_strings(dirname,corename,synctex_suffix,NULL);
378 if(quoted_corename && strlen(quoted_corename)>0) {
379 quoted = _synctex_merge_strings(dirname,quoted_corename,synctex_suffix,NULL);
382 none = _synctex_merge_strings(corename,synctex_suffix,NULL);
383 if(quoted_corename && strlen(quoted_corename)>0) {
384 quoted = _synctex_merge_strings(quoted_corename,synctex_suffix,NULL);
387 if(!_synctex_path_is_absolute(output) && build_directory && (size = strlen(build_directory))) {
388 temp = build_directory + size - 1;
389 if(_synctex_path_is_absolute(temp)) {
390 build = _synctex_merge_strings(build_directory,none,NULL);
391 if(quoted_corename && strlen(quoted_corename)>0) {
392 build_quoted = _synctex_merge_strings(build_directory,quoted,NULL);
395 build = _synctex_merge_strings(build_directory,"/",none,NULL);
396 if(quoted_corename && strlen(quoted_corename)>0) {
397 build_quoted = _synctex_merge_strings(build_directory,"/",quoted,NULL);
402 gz = _synctex_merge_strings(none,synctex_suffix_gz,NULL);
405 quoted_gz = _synctex_merge_strings(quoted,synctex_suffix_gz,NULL);
408 build_gz = _synctex_merge_strings(build,synctex_suffix_gz,NULL);
411 build_quoted_gz = _synctex_merge_strings(build_quoted,synctex_suffix_gz,NULL);
413 /* All the others names are properly set up... */
414 /* retain the most recently modified file */
415 # define TEST(FILENAME,COMPRESS_MODE) \
417 if (stat(FILENAME, &buf)) { \
421 if(buf.st_mtime>time) { \
423 synctex_name = FILENAME; \
424 compress_mode = COMPRESS_MODE; \
428 TEST(none,synctex_compress_mode_none);
429 TEST(gz,synctex_compress_mode_gz);
430 TEST(quoted,synctex_compress_mode_none);
431 TEST(quoted_gz,synctex_compress_mode_gz);
432 TEST(build,synctex_compress_mode_none);
433 TEST(build_gz,synctex_compress_mode_gz);
434 TEST(build_quoted,synctex_compress_mode_none);
435 TEST(build_quoted_gz,synctex_compress_mode_gz);
437 /* Free all the intermediate filenames, except the one that will be used as returned value. */
438 # define CLEAN_AND_REMOVE(FILENAME) \
439 if(FILENAME && (FILENAME!=synctex_name)) {\
441 printf("synctex tool info: %s removed\n",FILENAME);\
445 CLEAN_AND_REMOVE(none);
446 CLEAN_AND_REMOVE(gz);
447 CLEAN_AND_REMOVE(quoted);
448 CLEAN_AND_REMOVE(quoted_gz);
449 CLEAN_AND_REMOVE(build);
450 CLEAN_AND_REMOVE(build_gz);
451 CLEAN_AND_REMOVE(build_quoted);
452 CLEAN_AND_REMOVE(build_quoted_gz);
453 # undef CLEAN_AND_REMOVE
456 return -1;/* bad argument */
458 # undef compress_mode