]> www.fi.muni.cz Git - evince.git/blob - cut-n-paste/synctex/synctex_parser_utils.c
ee0544389ce650bda1acc0709be40550e95de67c
[evince.git] / cut-n-paste / synctex / synctex_parser_utils.c
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: Fri Mar 11 07:39:12 UTC 2011
7
8 Version: 1.13
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 #ifdef _WIN32_WINNT_WINXP
61 #define SYNCTEX_RECENT_WINDOWS 1
62 #endif
63
64 #ifdef SYNCTEX_WINDOWS
65 #include <windows.h>
66 #endif
67
68 void *_synctex_malloc(size_t size) {
69         void * ptr = malloc(size);
70         if(ptr) {
71 /*  There used to be a switch to use bzero because it is more secure. JL */
72                 memset(ptr,0, size);
73         }
74         return (void *)ptr;
75 }
76
77 int _synctex_error(const char * reason,...) {
78         va_list arg;
79         int result;
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.
89         JL.*/
90                 char *buff;
91                 size_t len;
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");
98                 free(buff);
99         }
100 #   else
101         result = fprintf(stderr,"SyncTeX ERROR: ");
102         result += vfprintf(stderr, reason, arg);
103         result += fprintf(stderr,"\n");
104 #   endif
105         va_end (arg);
106         return result;
107 }
108
109 /*  strip the last extension of the given string, this string is modified! */
110 void _synctex_strip_last_path_extension(char * string) {
111         if(NULL != string){
112                 char * last_component = NULL;
113                 char * last_extension = NULL;
114                 char * next = NULL;
115                 /*  first we find the last path component */
116                 if(NULL == (last_component = strstr(string,"/"))){
117                         last_component = string;
118                 } else {
119                         ++last_component;
120                         while((next = strstr(last_component,"/"))){
121                                 last_component = next+1;
122                         }
123                 }
124 #       ifdef   SYNCTEX_WINDOWS
125                 /*  On Windows, the '\' is also a path separator. */
126                 while((next = strstr(last_component,"\\"))){
127                         last_component = next+1;
128                 }
129 #       endif
130                 /*  then we find the last path extension */
131                 if((last_extension = strstr(last_component,"."))){
132                         ++last_extension;
133                         while((next = strstr(last_extension,"."))){
134                                 last_extension = next+1;
135                         }
136                         --last_extension;/*  back to the "." */
137                         if(last_extension>last_component){/*  filter out paths like ....my/dir/.hidden"*/
138                                 last_extension[0] = '\0';
139                         }
140                 }
141         }
142 }
143
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) {
146 #       if SYNCTEX_WINDOWS
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. */
152 next_character:
153         if(SYNCTEX_IS_PATH_SEPARATOR(*lhs)) {/*  lhs points to a path separator */
154                 if(!SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/*  but not rhs */
155                         return synctex_NO;
156                 }
157         } else if(SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/*  rhs points to a path separator but not lhs */
158                 return synctex_NO;
159         } else if(toupper(*lhs) != toupper(*rhs)){/*  uppercase do not match */
160                 return synctex_NO;
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 */
164                 return synctex_NO;
165         }
166         ++lhs;
167         ++rhs;
168         goto next_character;
169 #       else
170     return 0 == strcmp(lhs,rhs)?synctex_YES:synctex_NO;
171 #       endif
172 }
173
174 synctex_bool_t _synctex_path_is_absolute(const char * name) {
175         if(!strlen(name)) {
176                 return synctex_NO;
177         }
178 #       if SYNCTEX_WINDOWS
179         if(strlen(name)>2) {
180                 return (name[1]==':' && SYNCTEX_IS_PATH_SEPARATOR(name[2]))?synctex_YES:synctex_NO;
181         }
182         return synctex_NO;
183 #       else
184     return SYNCTEX_IS_PATH_SEPARATOR(name[0])?synctex_YES:synctex_NO;
185 #       endif
186 }
187
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);
191         if(c>name) {
192                 if(!SYNCTEX_IS_PATH_SEPARATOR(*c)) {
193                         do {
194                                 --c;
195                                 if(SYNCTEX_IS_PATH_SEPARATOR(*c)) {
196                                         return c+1;
197                                 }
198                         } while(c>name);
199                 }
200                 return c;/* the last path component is the void string*/
201         }
202         return c;
203 }
204
205 int _synctex_copy_with_quoting_last_path_component(const char * src, char ** dest_ref, size_t size) {
206   const char * lpc;
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);
211                 if(strlen(lpc)) {
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");
222                                                         free(dest);
223                                                         dest = NULL;/*  Don't forget to reinitialize. */
224                                                         return -2;
225                                                 }
226                                                 memmove(dpc+1,dpc,strlen(dpc)+1);       /*      Also move the null terminating character. */
227                                                 dpc[0]='"';
228                                                 dpc[strlen(dpc)+1]='\0';/*      Consistency test */
229                                                 dpc[strlen(dpc)]='"';
230                                                 return 0;       /*      Success. */
231                                         }
232                                         return -1;      /*      Memory allocation error.        */
233                                 }
234                                 _synctex_error("!  _synctex_copy_with_quoting_last_path_component: Internal inconsistency");
235                                 return -3;
236                         }
237                         return 0;       /*      Success. */
238                 }
239                 return 0;       /*      No last path component. */
240 #               undef dest
241         }
242         return 1; /*  Bad parameter, this value is subject to changes. */
243 }
244
245 /*  The client is responsible of the management of the returned string, if any. */
246 char * _synctex_merge_strings(const char * first,...);
247
248 char * _synctex_merge_strings(const char * first,...) {
249         va_list arg;
250         size_t size = 0;
251         const char * temp;
252         /*   First retrieve the size necessary to store the merged string */
253         va_start (arg, first);
254         temp = first;
255         do {
256                 size_t len = strlen(temp);
257                 if(UINT_MAX-len<size) {
258                         _synctex_error("!  _synctex_merge_strings: Capacity exceeded.");
259                         return NULL;
260                 }
261                 size+=len;
262         } while( (temp = va_arg(arg, const char *)) != NULL);
263         va_end(arg);
264         if(size>0) {
265                 char * result = NULL;
266                 ++size;
267                 /*  Create the memory storage */
268                 if(NULL!=(result = (char *)malloc(size))) {
269                         char * dest = result;
270                         va_start (arg, first);
271                         temp = first;
272                         do {
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");
277                                                 free(result);
278                                                 result = NULL;
279                                                 return NULL;
280                                         }
281                                         dest += size;
282                                 }
283                         } while( (temp = va_arg(arg, const char *)) != NULL);
284                         va_end(arg);
285                         dest[0]='\0';/*  Terminate the merged string */
286                         return result;
287                 }
288                 _synctex_error("!  _synctex_merge_strings: Memory problem");
289                 return NULL;
290         }
291         return NULL;    
292 }
293
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.
297  */
298 int _synctex_get_name(const char * output, const char * build_directory, char ** synctex_name_ref, synctex_io_mode_t * io_mode_ref)
299 {
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 */
302                 size_t size = 0;
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) {
308                         /*  Yes, we do. */
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;
321                         struct stat buf;
322                         time_t the_time = 0;
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");
330                                         return -1;
331                                 }
332                                 if(core_name != strncpy(core_name,base_name,size)) {
333                                         _synctex_error("!  _synctex_get_name: Copy problem 1");
334                                         free(core_name);
335                                         dir_name = NULL;
336                                         return -2;
337                                 }
338                                 core_name[size] = '\0';
339                         } else {
340                                 /*  There is no path extension,
341                                  *  Just make a copy of base_name */
342                                 core_name = _synctex_merge_strings(base_name);
343                         }
344                         /*  core_name is properly set up, owned by "self". */
345                         /*  creating dir_name. */
346                         size = strlen(output)-strlen(base_name);
347                         if(size>0) {
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");
351                                         free(core_name);
352                                         dir_name = NULL;
353                                         return -1;
354                                 }
355                                 if(dir_name != strncpy(dir_name,output,size)) {
356                                         _synctex_error("!  _synctex_get_name: Copy problem");
357                                         free(dir_name);
358                                         dir_name = NULL;
359                                         free(core_name);
360                                         dir_name = NULL;
361                                         return -2;
362                                 }
363                                 dir_name[size] = '\0';
364                         }
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,"\"");
369                         }
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);
375                                 }
376                         } else {
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);
380                                 }
381                         }
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);
388                                         }
389                                 } else {
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);
393                                         }
394                                 }
395                         }
396                         if(basic_name) {
397                                 gz_name = _synctex_merge_strings(basic_name,synctex_suffix_gz,NULL);
398                         }
399                         if(quoted_name) {
400                                 quoted_gz_name = _synctex_merge_strings(quoted_name,synctex_suffix_gz,NULL);
401                         }
402                         if(build_name) {
403                                 build_gz_name = _synctex_merge_strings(build_name,synctex_suffix_gz,NULL);
404                         }
405                         if(build_quoted_name) {
406                                 build_quoted_gz_name = _synctex_merge_strings(build_quoted_name,synctex_suffix_gz,NULL);
407                         }
408                         /*  All the others names are properly set up... */
409                         /*  retain the most recently modified file */
410 #                       define TEST(FILENAME,COMPRESS_MODE) \
411                         if(FILENAME) {\
412                                 if (stat(FILENAME, &buf)) { \
413                                         free(FILENAME);\
414                                         FILENAME = NULL;\
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; \
420                     } else { \
421                         io_mode &= ~synctex_io_gz_mask; \
422                     } \
423                                 } \
424                         }
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);
433 #                       undef TEST
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)) {\
437                                 remove(FILENAME);\
438                                 printf("synctex tool info: %s removed\n",FILENAME);\
439                                 free(FILENAME);\
440                                 FILENAME = NULL;\
441                         }
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;
454                         return 0;
455                 }
456                 return -1;/*  bad argument */
457         }
458         return -2;
459 }
460
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];
465 }