dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

synctex_parser_utils.c (19349B)


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