]> www.fi.muni.cz Git - evince.git/blob - cut-n-paste/synctex/synctex_parser_utils.c
Revert "cut-n-paste: update synctex_parser to version 1.13"
[evince.git] / cut-n-paste / synctex / synctex_parser_utils.c
1 /* 
2 Copyright (c) 2008, 2009 jerome DOT laurens AT u-bourgogne DOT fr
3
4 This file is part of the SyncTeX package.
5
6 Latest Revision: Wed Nov  4 11:52:35 UTC 2009
7
8 Version: 1.9
9 See synctex_parser_readme.txt for more details
10
11 License:
12 --------
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
20 conditions:
21
22 The above copyright notice and this permission notice shall be
23 included in all copies or substantial portions of the Software.
24
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
33
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.
38
39 */
40
41 /*  In this file, we find all the functions that may depend on the operating system. */
42
43 #include <synctex_parser_utils.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <stdarg.h>
47 #include <stdio.h>
48
49 #include <limits.h>
50 #include <ctype.h>
51 #include <string.h>
52
53 #include <sys/stat.h>
54
55 #if defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
56 #define SYNCTEX_WINDOWS 1
57 #endif
58
59 #ifdef _WIN32_WINNT_WINXP
60 #define SYNCTEX_RECENT_WINDOWS 1
61 #endif
62
63 #ifdef SYNCTEX_WINDOWS
64 #include <windows.h>
65 #endif
66
67 void *_synctex_malloc(size_t size) {
68         void * ptr = malloc(size);
69         if(ptr) {
70 /*  There used to be a switch to use bzero because it is more secure. JL */
71                 memset(ptr,0, size);
72         }
73         return (void *)ptr;
74 }
75
76 int _synctex_error(const char * reason,...) {
77         va_list arg;
78         int result;
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.
88         JL.*/
89                 char *buff;
90                 size_t len;
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");
97                 free(buff);
98         }
99 #   else
100         result = fprintf(stderr,"SyncTeX ERROR: ");
101         result += vfprintf(stderr, reason, arg);
102         result += fprintf(stderr,"\n");
103 #   endif
104         va_end (arg);
105         return result;
106 }
107
108 /*  strip the last extension of the given string, this string is modified! */
109 void _synctex_strip_last_path_extension(char * string) {
110         if(NULL != string){
111                 char * last_component = NULL;
112                 char * last_extension = NULL;
113                 char * next = NULL;
114                 /*  first we find the last path component */
115                 if(NULL == (last_component = strstr(string,"/"))){
116                         last_component = string;
117                 } else {
118                         ++last_component;
119                         while((next = strstr(last_component,"/"))){
120                                 last_component = next+1;
121                         }
122                 }
123 #       ifdef   SYNCTEX_WINDOWS
124                 /*  On Windows, the '\' is also a path separator. */
125                 while((next = strstr(last_component,"\\"))){
126                         last_component = next+1;
127                 }
128 #       endif
129                 /*  then we find the last path extension */
130                 if((last_extension = strstr(last_component,"."))){
131                         ++last_extension;
132                         while((next = strstr(last_extension,"."))){
133                                 last_extension = next+1;
134                         }
135                         --last_extension;/*  back to the "." */
136                         if(last_extension>last_component){/*  filter out paths like ....my/dir/.hidden"*/
137                                 last_extension[0] = '\0';
138                         }
139                 }
140         }
141 }
142
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) {
145 #       if SYNCTEX_WINDOWS
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. */
151 next_character:
152         if(SYNCTEX_IS_PATH_SEPARATOR(*lhs)) {/*  lhs points to a path separator */
153                 if(!SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/*  but not rhs */
154                         return synctex_NO;
155                 }
156         } else if(SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/*  rhs points to a path separator but not lhs */
157                 return synctex_NO;
158         } else if(toupper(*lhs) != toupper(*rhs)){/*  uppercase do not match */
159                 return synctex_NO;
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 */
163                 return synctex_NO;
164         }
165         ++lhs;
166         ++rhs;
167         goto next_character;
168 #       else
169     return 0 == strcmp(lhs,rhs)?synctex_YES:synctex_NO;
170 #       endif
171 }
172
173 synctex_bool_t _synctex_path_is_absolute(const char * name) {
174         if(!strlen(name)) {
175                 return synctex_NO;
176         }
177 #       if SYNCTEX_WINDOWS
178         if(strlen(name)>2) {
179                 return (name[1]==':' && SYNCTEX_IS_PATH_SEPARATOR(name[2]))?synctex_YES:synctex_NO;
180         }
181         return synctex_NO;
182 #       else
183     return SYNCTEX_IS_PATH_SEPARATOR(name[0])?synctex_YES:synctex_NO;
184 #       endif
185 }
186
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);
190         if(c>name) {
191                 if(!SYNCTEX_IS_PATH_SEPARATOR(*c)) {
192                         do {
193                                 --c;
194                                 if(SYNCTEX_IS_PATH_SEPARATOR(*c)) {
195                                         return c+1;
196                                 }
197                         } while(c>name);
198                 }
199                 return c;/* the last path component is the void string*/
200         }
201         return c;
202 }
203
204 int _synctex_copy_with_quoting_last_path_component(const char * src, char ** dest_ref, size_t size) {
205   const char * lpc;
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);
210                 if(strlen(lpc)) {
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");
221                                                         free(dest);
222                                                         dest = NULL;/*  Don't forget to reinitialize. */
223                                                         return -2;
224                                                 }
225                                                 memmove(dpc+1,dpc,strlen(dpc)+1);       /*      Also move the null terminating character. */
226                                                 dpc[0]='"';
227                                                 dpc[strlen(dpc)+1]='\0';/*      Consistency test */
228                                                 dpc[strlen(dpc)]='"';
229                                                 return 0;       /*      Success. */
230                                         }
231                                         return -1;      /*      Memory allocation error.        */
232                                 }
233                                 _synctex_error("!  _synctex_copy_with_quoting_last_path_component: Internal inconsistency");
234                                 return -3;
235                         }
236                         return 0;       /*      Success. */
237                 }
238                 return 0;       /*      No last path component. */
239 #               undef dest
240         }
241         return 1; /*  Bad parameter, this value is subject to changes. */
242 }
243
244 /*  The client is responsible of the management of the returned string, if any. */
245 char * _synctex_merge_strings(const char * first,...);
246
247 char * _synctex_merge_strings(const char * first,...) {
248         va_list arg;
249         size_t size = 0;
250         const char * temp;
251         /*   First retrieve the size necessary to store the merged string */
252         va_start (arg, first);
253         temp = first;
254         do {
255                 size_t len = strlen(temp);
256                 if(UINT_MAX-len<size) {
257                         _synctex_error("!  _synctex_merge_strings: Capacity exceeded.");
258                         return NULL;
259                 }
260                 size+=len;
261         } while( (temp = va_arg(arg, const char *)) != NULL);
262         va_end(arg);
263         if(size>0) {
264                 char * result = NULL;
265                 ++size;
266                 /*  Create the memory storage */
267                 if(NULL!=(result = (char *)malloc(size))) {
268                         char * dest = result;
269                         va_start (arg, first);
270                         temp = first;
271                         do {
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");
276                                                 free(result);
277                                                 result = NULL;
278                                                 return NULL;
279                                         }
280                                         dest += size;
281                                 }
282                         } while( (temp = va_arg(arg, const char *)) != NULL);
283                         va_end(arg);
284                         dest[0]='\0';/*  Terminate the merged string */
285                         return result;
286                 }
287                 _synctex_error("!  _synctex_merge_strings: Memory problem");
288                 return NULL;
289         }
290         return NULL;    
291 }
292
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.
296  */
297 int _synctex_get_name(const char * output, const char * build_directory, char ** synctex_name_ref, synctex_compress_mode_t * compress_mode_ref)
298 {
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*/
304                 size_t size = 0;
305                 /*  Initialize the return values. */
306                 synctex_name = NULL;
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) {
311                         /*  Yes, we do. */
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;
316                         char * none = NULL;
317                         char * gz = NULL;
318                         char * quoted = NULL;
319                         char * quoted_gz = NULL;
320                         char * build = NULL;
321                         char * build_gz = NULL;
322                         char * build_quoted = NULL;
323                         char * build_quoted_gz = NULL;
324                         struct stat buf;
325                         time_t time = 0;
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;
331                         if(size>0) {
332                                 /*  dot properly found, now create corename  */
333                                 if(NULL == (corename = (char *)malloc(size+1))) {
334                                         _synctex_error("!  _synctex_get_name: Memory problem 1");
335                                         return -1;
336                                 }
337                                 if(corename != strncpy(corename,basename,size)) {
338                                         _synctex_error("!  _synctex_get_name: Copy problem 1");
339                                         free(corename);
340                                         dirname = NULL;
341                                         return -2;
342                                 }
343                                 corename[size] = '\0';
344                         } else {
345                                 /*  There is no path extension,
346                                  *  Just make a copy of basename */
347                                 corename = _synctex_merge_strings(basename);
348                         }
349                         /*  corename is properly set up, owned by "self". */
350                         /*  creating dirname. */
351                         size = strlen(output)-strlen(basename);
352                         if(size>0) {
353                                 /*  output contains more than one path component */
354                                 if(NULL == (dirname = (char *)malloc(size+1))) {
355                                         _synctex_error("!  _synctex_get_name: Memory problem");
356                                         free(corename);
357                                         dirname = NULL;
358                                         return -1;
359                                 }
360                                 if(dirname != strncpy(dirname,output,size)) {
361                                         _synctex_error("!  _synctex_get_name: Copy problem");
362                                         free(dirname);
363                                         dirname = NULL;
364                                         free(corename);
365                                         dirname = NULL;
366                                         return -2;
367                                 }
368                                 dirname[size] = '\0';
369                         }
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,"\"");
374                         }
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);
380                                 }
381                         } else {
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);
385                                 }
386                         }
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);
393                                         }
394                                 } else {
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);
398                                         }
399                                 }
400                         }
401                         if(none) {
402                                 gz = _synctex_merge_strings(none,synctex_suffix_gz,NULL);
403                         }
404                         if(quoted) {
405                                 quoted_gz =     _synctex_merge_strings(quoted,synctex_suffix_gz,NULL);
406                         }
407                         if(build) {
408                                 build_gz = _synctex_merge_strings(build,synctex_suffix_gz,NULL);
409                         }
410                         if(build_quoted) {
411                                 build_quoted_gz = _synctex_merge_strings(build_quoted,synctex_suffix_gz,NULL);
412                         }
413                         /*  All the others names are properly set up... */
414                         /*  retain the most recently modified file */
415 #                       define TEST(FILENAME,COMPRESS_MODE) \
416                         if(FILENAME) {\
417                                 if (stat(FILENAME, &buf)) { \
418                                         free(FILENAME);\
419                                         FILENAME = NULL;\
420                                 } else { \
421                                         if(buf.st_mtime>time) { \
422                                                 time=buf.st_mtime; \
423                                                 synctex_name = FILENAME; \
424                                                 compress_mode = COMPRESS_MODE; \
425                                         } \
426                                 } \
427                         }
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);
436 #                       undef TEST
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)) {\
440                                 remove(FILENAME);\
441                                 printf("synctex tool info: %s removed\n",FILENAME);\
442                                 free(FILENAME);\
443                                 FILENAME = NULL;\
444                         }
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
454                         return 0;
455                 }
456                 return -1;/*  bad argument */
457 #               undef synctex_name
458 #               undef compress_mode 
459         }
460         return -2;
461 }
462