File indexing completed on 2024-05-12 05:46:56
0001 /* 0002 Copyright (c) 2008-2017 jerome DOT laurens AT u-bourgogne DOT fr 0003 0004 This file is part of the __SyncTeX__ package. 0005 0006 [//]: # (Latest Revision: Fri Jul 14 16:20:41 UTC 2017) 0007 [//]: # (Version: 1.19) 0008 0009 See `synctex_parser_readme.md` for more details 0010 0011 ## License 0012 0013 Permission is hereby granted, free of charge, to any person 0014 obtaining a copy of this software and associated documentation 0015 files (the "Software"), to deal in the Software without 0016 restriction, including without limitation the rights to use, 0017 copy, modify, merge, publish, distribute, sublicense, and/or sell 0018 copies of the Software, and to permit persons to whom the 0019 Software is furnished to do so, subject to the following 0020 conditions: 0021 0022 The above copyright notice and this permission notice shall be 0023 included in all copies or substantial portions of the Software. 0024 0025 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 0026 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 0027 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 0028 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 0029 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 0030 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 0031 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 0032 OTHER DEALINGS IN THE SOFTWARE 0033 0034 Except as contained in this notice, the name of the copyright holder 0035 shall not be used in advertising or otherwise to promote the sale, 0036 use or other dealings in this Software without prior written 0037 authorization from the copyright holder. 0038 0039 */ 0040 0041 /* In this file, we find all the functions that may depend on the operating system. */ 0042 0043 #include <stdarg.h> 0044 #include <stdio.h> 0045 #include <stdlib.h> 0046 #include <string.h> 0047 #include <synctex_parser_utils.h> 0048 0049 #include <ctype.h> 0050 #include <limits.h> 0051 #include <string.h> 0052 0053 #include <sys/stat.h> 0054 0055 #if defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) 0056 #define SYNCTEX_WINDOWS 1 0057 #endif 0058 0059 #if defined(__OS2__) 0060 #define SYNCTEX_OS2 1 0061 #endif 0062 0063 #if defined(_WIN32) 0064 #define SYNCTEX_RECENT_WINDOWS 1 0065 #endif 0066 0067 #ifdef SYNCTEX_WINDOWS 0068 #include <shlwapi.h> /* Use shlwapi.lib */ 0069 #include <windows.h> 0070 #endif 0071 0072 void *_synctex_malloc(size_t size) 0073 { 0074 void *ptr = malloc(size); 0075 if (ptr) { 0076 memset(ptr, 0, size); /* ensures null termination of strings */ 0077 } 0078 return (void *)ptr; 0079 } 0080 0081 void _synctex_free(void *ptr) 0082 { 0083 if (ptr) { 0084 free(ptr); 0085 } 0086 } 0087 0088 #if !defined(_WIN32) 0089 #include <syslog.h> 0090 #endif 0091 0092 int _synctex_log(int level, const char *prompt, const char *reason, ...) SYNCTEX_PRINTF_FORMAT(3, 4); 0093 int _synctex_log(int level, const char *prompt, const char *reason, ...) 0094 { 0095 va_list arg; 0096 int result; 0097 va_start(arg, reason); 0098 #ifdef SYNCTEX_RECENT_WINDOWS 0099 { /* This code is contributed by William Blum. 0100 As it does not work on some older computers, 0101 the _WIN32 conditional here is replaced with a SYNCTEX_RECENT_WINDOWS one. 0102 According to http://msdn.microsoft.com/en-us/library/aa363362(VS.85).aspx 0103 Minimum supported client Windows 2000 Professional 0104 Minimum supported server Windows 2000 Server 0105 People running Windows 2K standard edition will not have OutputDebugStringA. 0106 JL.*/ 0107 char *buff; 0108 size_t len; 0109 OutputDebugStringA(prompt); 0110 #ifdef _MSC_VER 0111 len = _vscprintf(reason, arg) + 1; 0112 buff = (char *)malloc(len * sizeof(char)); 0113 #else /* MinGW */ 0114 size_t buffersize = 1024; 0115 size_t max_buffersize = 1024 * buffersize; 0116 int result; 0117 buff = (char *)malloc(buffersize * sizeof(char)); 0118 result = _vsnprintf(buff, buffersize - 1, reason, arg); 0119 while (-1 == result && buffersize <= max_buffersize) { 0120 buffersize = buffersize * 2; 0121 buff = (char *)realloc(buff, buffersize * sizeof(char)); 0122 result = _vsnprintf(buff, buffersize - 1, reason, arg); 0123 } 0124 if (-1 == result) { 0125 // could not make the buffer big enough or simply could not write to it 0126 free(buff); 0127 return -1; 0128 } 0129 #endif 0130 result = vsprintf(buff, reason, arg) + strlen(prompt); 0131 OutputDebugStringA(buff); 0132 OutputDebugStringA("\n"); 0133 free(buff); 0134 } 0135 #elif defined SYNCTEX_USE_SYSLOG 0136 char *buffer1 = NULL; 0137 char *buffer2 = NULL; 0138 openlog("SyncTeX", LOG_CONS | LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_LOCAL0); 0139 if (vasprintf(&buffer1, reason, arg) >= 0 && asprintf(&buffer2, "%s%s", prompt, buffer1) >= 0) { 0140 syslog(level, "%s", buffer2); 0141 result = (int)strlen(buffer2); 0142 } else { 0143 syslog(level, "%s", prompt); 0144 vsyslog(level, reason, arg); 0145 result = (int)strlen(prompt); 0146 } 0147 free(buffer1); 0148 free(buffer2); 0149 closelog(); 0150 #else 0151 FILE *where = level == LOG_ERR ? stderr : stdout; 0152 result = fputs(prompt, where); 0153 result += vfprintf(where, reason, arg); 0154 result += fprintf(where, "\n"); 0155 #endif 0156 va_end(arg); 0157 return result; 0158 } 0159 0160 int _synctex_error(const char *reason, ...) 0161 { 0162 va_list arg; 0163 int result; 0164 va_start(arg, reason); 0165 #if defined(SYNCTEX_RECENT_WINDOWS) /* LOG_ERR is not used */ 0166 result = _synctex_log(0, "! SyncTeX Error : ", reason, arg); 0167 #else 0168 result = _synctex_log(LOG_ERR, "! SyncTeX Error : ", reason, arg); 0169 #endif 0170 va_end(arg); 0171 return result; 0172 } 0173 0174 int _synctex_debug(const char *reason, ...) 0175 { 0176 va_list arg; 0177 int result; 0178 va_start(arg, reason); 0179 #if defined(SYNCTEX_RECENT_WINDOWS) /* LOG_DEBUG is not used */ 0180 result = _synctex_log(0, "! SyncTeX Error : ", reason, arg); 0181 #else 0182 result = _synctex_log(LOG_DEBUG, "! SyncTeX Error : ", reason, arg); 0183 #endif 0184 va_end(arg); 0185 return result; 0186 } 0187 0188 /* strip the last extension of the given string, this string is modified! */ 0189 void _synctex_strip_last_path_extension(char *string) 0190 { 0191 if (NULL != string) { 0192 char *last_component = NULL; 0193 char *last_extension = NULL; 0194 #if defined(SYNCTEX_WINDOWS) 0195 last_component = PathFindFileNameA(string); 0196 last_extension = PathFindExtensionA(string); 0197 if (last_extension == NULL) 0198 return; 0199 if (last_component == NULL) 0200 last_component = string; 0201 if (last_extension > last_component) { /* filter out paths like "my/dir/.hidden" */ 0202 last_extension[0] = '\0'; 0203 } 0204 #else 0205 char *next = NULL; 0206 /* first we find the last path component */ 0207 if (NULL == (last_component = strstr(string, "/"))) { 0208 last_component = string; 0209 } else { 0210 ++last_component; 0211 while ((next = strstr(last_component, "/"))) { 0212 last_component = next + 1; 0213 } 0214 } 0215 #if defined(SYNCTEX_OS2) 0216 /* On OS2, the '\' is also a path separator. */ 0217 while ((next = strstr(last_component, "\\"))) { 0218 last_component = next + 1; 0219 } 0220 #endif /* SYNCTEX_OS2 */ 0221 /* then we find the last path extension */ 0222 if ((last_extension = strstr(last_component, "."))) { 0223 ++last_extension; 0224 while ((next = strstr(last_extension, "."))) { 0225 last_extension = next + 1; 0226 } 0227 --last_extension; /* back to the "." */ 0228 if (last_extension > last_component) { /* filter out paths like ....my/dir/.hidden"*/ 0229 last_extension[0] = '\0'; 0230 } 0231 } 0232 #endif /* SYNCTEX_WINDOWS */ 0233 } 0234 } 0235 0236 synctex_bool_t synctex_ignore_leading_dot_slash_in_path(const char **name_ref) 0237 { 0238 if (SYNCTEX_IS_DOT((*name_ref)[0]) && SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[1])) { 0239 do { 0240 (*name_ref) += 2; 0241 while (SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[0])) { 0242 ++(*name_ref); 0243 } 0244 } while (SYNCTEX_IS_DOT((*name_ref)[0]) && SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[1])); 0245 return synctex_YES; 0246 } 0247 return synctex_NO; 0248 } 0249 0250 /* The base name is necessary to deal with the 2011 file naming convention... 0251 * path is a '\0' terminated string 0252 * The return value is the trailing part of the argument, 0253 * just following the first occurrence of the regexp pattern "[^|/|\].[\|/]+".*/ 0254 const char *_synctex_base_name(const char *path) 0255 { 0256 const char *ptr = path; 0257 do { 0258 if (synctex_ignore_leading_dot_slash_in_path(&ptr)) { 0259 return ptr; 0260 } 0261 do { 0262 if (!*(++ptr)) { 0263 return path; 0264 } 0265 } while (!SYNCTEX_IS_PATH_SEPARATOR(*ptr)); 0266 } while (*(++ptr)); 0267 return path; 0268 } 0269 0270 /* Compare two file names, windows is sometimes case insensitive... */ 0271 synctex_bool_t _synctex_is_equivalent_file_name(const char *lhs, const char *rhs) 0272 { 0273 /* Remove the leading regex '(\./+)*' in both rhs and lhs */ 0274 synctex_ignore_leading_dot_slash_in_path(&lhs); 0275 synctex_ignore_leading_dot_slash_in_path(&rhs); 0276 next_character: 0277 if (SYNCTEX_IS_PATH_SEPARATOR(*lhs)) { /* lhs points to a path separator */ 0278 if (!SYNCTEX_IS_PATH_SEPARATOR(*rhs)) { /* but not rhs */ 0279 return synctex_NO; 0280 } 0281 ++lhs; 0282 ++rhs; 0283 synctex_ignore_leading_dot_slash_in_path(&lhs); 0284 synctex_ignore_leading_dot_slash_in_path(&rhs); 0285 goto next_character; 0286 } else if (SYNCTEX_IS_PATH_SEPARATOR(*rhs)) { /* rhs points to a path separator but not lhs */ 0287 return synctex_NO; 0288 } else if (SYNCTEX_ARE_PATH_CHARACTERS_EQUAL(*lhs, *rhs)) { /* uppercase do not match */ 0289 return synctex_NO; 0290 } else if (!*lhs) { /* lhs is at the end of the string */ 0291 return *rhs ? synctex_NO : synctex_YES; 0292 } else if (!*rhs) { /* rhs is at the end of the string but not lhs */ 0293 return synctex_NO; 0294 } 0295 ++lhs; 0296 ++rhs; 0297 goto next_character; 0298 } 0299 0300 synctex_bool_t _synctex_path_is_absolute(const char *name) 0301 { 0302 if (!strlen(name)) { 0303 return synctex_NO; 0304 } 0305 #if defined(SYNCTEX_WINDOWS) || defined(SYNCTEX_OS2) 0306 if (strlen(name) > 2) { 0307 return (name[1] == ':' && SYNCTEX_IS_PATH_SEPARATOR(name[2])) ? synctex_YES : synctex_NO; 0308 } 0309 return synctex_NO; 0310 #else 0311 return SYNCTEX_IS_PATH_SEPARATOR(name[0]) ? synctex_YES : synctex_NO; 0312 #endif 0313 } 0314 0315 /* We do not take care of UTF-8 */ 0316 const char *_synctex_last_path_component(const char *name) 0317 { 0318 const char *c = name + strlen(name); 0319 if (c > name) { 0320 if (!SYNCTEX_IS_PATH_SEPARATOR(*c)) { 0321 do { 0322 --c; 0323 if (SYNCTEX_IS_PATH_SEPARATOR(*c)) { 0324 return c + 1; 0325 } 0326 } while (c > name); 0327 } 0328 return c; /* the last path component is the void string*/ 0329 } 0330 return c; 0331 } 0332 0333 int _synctex_copy_with_quoting_last_path_component(const char *src, char **dest_ref, size_t size) 0334 { 0335 if (src && dest_ref) { 0336 const char *lpc; 0337 #define dest (*dest_ref) 0338 dest = NULL; /* Default behavior: no change and success. */ 0339 lpc = _synctex_last_path_component(src); 0340 if (strlen(lpc)) { 0341 if (strchr(lpc, ' ') && lpc[0] != '"' && lpc[strlen(lpc) - 1] != '"') { 0342 /* We are in the situation where adding the quotes is allowed. */ 0343 /* Time to add the quotes. */ 0344 /* Consistency test: we must have dest+size>dest+strlen(dest)+2 0345 * or equivalently: strlen(dest)+2<size (see below) */ 0346 if (strlen(src) < size) { 0347 if ((dest = (char *)malloc(size + 2))) { 0348 char *dpc = dest + (lpc - src); /* dpc is the last path component of dest. */ 0349 if (dest != strncpy(dest, src, size)) { 0350 _synctex_error("! _synctex_copy_with_quoting_last_path_component: Copy problem"); 0351 free(dest); 0352 dest = NULL; /* Don't forget to reinitialize. */ 0353 return -2; 0354 } 0355 memmove(dpc + 1, dpc, strlen(dpc) + 1); /* Also move the null terminating character. */ 0356 dpc[0] = '"'; 0357 dpc[strlen(dpc) + 1] = '\0'; /* Consistency test */ 0358 dpc[strlen(dpc)] = '"'; 0359 return 0; /* Success. */ 0360 } 0361 return -1; /* Memory allocation error. */ 0362 } 0363 _synctex_error("! _synctex_copy_with_quoting_last_path_component: Internal inconsistency"); 0364 return -3; 0365 } 0366 return 0; /* Success. */ 0367 } 0368 return 0; /* No last path component. */ 0369 #undef dest 0370 } 0371 return 1; /* Bad parameter, this value is subject to changes. */ 0372 } 0373 0374 /* The client is responsible of the management of the returned string, if any. */ 0375 char *_synctex_merge_strings(const char *first, ...); 0376 0377 char *_synctex_merge_strings(const char *first, ...) 0378 { 0379 va_list arg; 0380 size_t size = 0; 0381 const char *temp; 0382 /* First retrieve the size necessary to store the merged string */ 0383 va_start(arg, first); 0384 temp = first; 0385 do { 0386 size_t len = strlen(temp); 0387 if (UINT_MAX - len < size) { 0388 _synctex_error("! _synctex_merge_strings: Capacity exceeded."); 0389 return NULL; 0390 } 0391 size += len; 0392 } while ((temp = va_arg(arg, const char *)) != NULL); 0393 va_end(arg); 0394 if (size > 0) { 0395 char *result = NULL; 0396 ++size; 0397 /* Create the memory storage */ 0398 if (NULL != (result = (char *)malloc(size))) { 0399 char *dest = result; 0400 va_start(arg, first); 0401 temp = first; 0402 do { 0403 if ((size = strlen(temp)) > 0) { 0404 /* There is something to merge */ 0405 if (dest != strncpy(dest, temp, size)) { 0406 _synctex_error("! _synctex_merge_strings: Copy problem"); 0407 free(result); 0408 result = NULL; 0409 return NULL; 0410 } 0411 dest += size; 0412 } 0413 } while ((temp = va_arg(arg, const char *)) != NULL); 0414 va_end(arg); 0415 dest[0] = '\0'; /* Terminate the merged string */ 0416 return result; 0417 } 0418 _synctex_error("! _synctex_merge_strings: Memory problem"); 0419 return NULL; 0420 } 0421 return NULL; 0422 } 0423 0424 /* The purpose of _synctex_get_name is to find the name of the synctex file. 0425 * There is a list of possible filenames from which we return the most recent one and try to remove all the others. 0426 * With two runs of pdftex or xetex we are sure the synctex file is really the most appropriate. 0427 */ 0428 int _synctex_get_name(const char *output, const char *build_directory, char **synctex_name_ref, synctex_io_mode_t *io_mode_ref) 0429 { 0430 if (output && synctex_name_ref && io_mode_ref) { 0431 /* If output is already absolute, we just have to manage the quotes and the compress mode */ 0432 size_t size = 0; 0433 char *synctex_name = NULL; 0434 synctex_io_mode_t io_mode = *io_mode_ref; 0435 const char *base_name = _synctex_last_path_component(output); /* do not free, output is the owner. base name of output*/ 0436 /* Do we have a real base name ? */ 0437 if (strlen(base_name) > 0) { 0438 /* Yes, we do. */ 0439 const char *temp = NULL; 0440 char *core_name = NULL; /* base name of output without path extension. */ 0441 char *dir_name = NULL; /* dir name of output */ 0442 char *quoted_core_name = NULL; 0443 char *basic_name = NULL; 0444 char *gz_name = NULL; 0445 char *quoted_name = NULL; 0446 char *quoted_gz_name = NULL; 0447 char *build_name = NULL; 0448 char *build_gz_name = NULL; 0449 char *build_quoted_name = NULL; 0450 char *build_quoted_gz_name = NULL; 0451 struct stat buf; 0452 time_t the_time = 0; 0453 /* Create core_name: let temp point to the dot before the path extension of base_name; 0454 * We start form the \0 terminating character and scan the string upward until we find a dot. 0455 * The leading dot is not accepted. */ 0456 if ((temp = strrchr(base_name, '.')) && (size = temp - base_name) > 0) { 0457 /* There is a dot and it is not at the leading position */ 0458 if (NULL == (core_name = (char *)malloc(size + 1))) { 0459 _synctex_error("! _synctex_get_name: Memory problem 1"); 0460 return -1; 0461 } 0462 if (core_name != strncpy(core_name, base_name, size)) { 0463 _synctex_error("! _synctex_get_name: Copy problem 1"); 0464 free(core_name); 0465 dir_name = NULL; 0466 return -2; 0467 } 0468 core_name[size] = '\0'; 0469 } else { 0470 /* There is no path extension, 0471 * Just make a copy of base_name */ 0472 core_name = _synctex_merge_strings(base_name); 0473 } 0474 /* core_name is properly set up, owned by "self". */ 0475 /* creating dir_name. */ 0476 size = strlen(output) - strlen(base_name); 0477 if (size > 0) { 0478 /* output contains more than one path component */ 0479 if (NULL == (dir_name = (char *)malloc(size + 1))) { 0480 _synctex_error("! _synctex_get_name: Memory problem"); 0481 free(core_name); 0482 return -1; 0483 } 0484 if (dir_name != strncpy(dir_name, output, size)) { 0485 _synctex_error("! _synctex_get_name: Copy problem"); 0486 free(dir_name); 0487 dir_name = NULL; 0488 free(core_name); 0489 dir_name = NULL; 0490 return -2; 0491 } 0492 dir_name[size] = '\0'; 0493 } 0494 /* dir_name is properly set up. It ends with a path separator, if non void. */ 0495 /* creating quoted_core_name. */ 0496 if (strchr(core_name, ' ')) { 0497 quoted_core_name = _synctex_merge_strings("\"", core_name, "\""); 0498 } 0499 /* quoted_core_name is properly set up. */ 0500 if (dir_name && strlen(dir_name) > 0) { 0501 basic_name = _synctex_merge_strings(dir_name, core_name, synctex_suffix, NULL); 0502 if (quoted_core_name && strlen(quoted_core_name) > 0) { 0503 quoted_name = _synctex_merge_strings(dir_name, quoted_core_name, synctex_suffix, NULL); 0504 } 0505 } else { 0506 basic_name = _synctex_merge_strings(core_name, synctex_suffix, NULL); 0507 if (quoted_core_name && strlen(quoted_core_name) > 0) { 0508 quoted_name = _synctex_merge_strings(quoted_core_name, synctex_suffix, NULL); 0509 } 0510 } 0511 if (!_synctex_path_is_absolute(output) && build_directory && (size = strlen(build_directory))) { 0512 temp = build_directory + size - 1; 0513 if (_synctex_path_is_absolute(temp)) { 0514 build_name = _synctex_merge_strings(build_directory, basic_name, NULL); 0515 if (quoted_core_name && strlen(quoted_core_name) > 0) { 0516 build_quoted_name = _synctex_merge_strings(build_directory, quoted_name, NULL); 0517 } 0518 } else { 0519 build_name = _synctex_merge_strings(build_directory, "/", basic_name, NULL); 0520 if (quoted_core_name && strlen(quoted_core_name) > 0) { 0521 build_quoted_name = _synctex_merge_strings(build_directory, "/", quoted_name, NULL); 0522 } 0523 } 0524 } 0525 if (basic_name) { 0526 gz_name = _synctex_merge_strings(basic_name, synctex_suffix_gz, NULL); 0527 } 0528 if (quoted_name) { 0529 quoted_gz_name = _synctex_merge_strings(quoted_name, synctex_suffix_gz, NULL); 0530 } 0531 if (build_name) { 0532 build_gz_name = _synctex_merge_strings(build_name, synctex_suffix_gz, NULL); 0533 } 0534 if (build_quoted_name) { 0535 build_quoted_gz_name = _synctex_merge_strings(build_quoted_name, synctex_suffix_gz, NULL); 0536 } 0537 /* All the others names are properly set up... */ 0538 /* retain the most recently modified file */ 0539 #define TEST(FILENAME, COMPRESS_MODE) \ 0540 if (FILENAME) { \ 0541 if (stat(FILENAME, &buf)) { \ 0542 free(FILENAME); \ 0543 FILENAME = NULL; \ 0544 } else if (buf.st_mtime > the_time) { \ 0545 the_time = buf.st_mtime; \ 0546 synctex_name = FILENAME; \ 0547 if (COMPRESS_MODE) { \ 0548 io_mode |= synctex_io_gz_mask; \ 0549 } else { \ 0550 io_mode &= ~synctex_io_gz_mask; \ 0551 } \ 0552 } \ 0553 } 0554 TEST(basic_name, synctex_DONT_COMPRESS); 0555 TEST(gz_name, synctex_COMPRESS); 0556 TEST(quoted_name, synctex_DONT_COMPRESS); 0557 TEST(quoted_gz_name, synctex_COMPRESS); 0558 TEST(build_name, synctex_DONT_COMPRESS); 0559 TEST(build_gz_name, synctex_COMPRESS); 0560 TEST(build_quoted_name, synctex_DONT_COMPRESS); 0561 TEST(build_quoted_gz_name, synctex_COMPRESS); 0562 #undef TEST 0563 /* Free all the intermediate filenames, except the one that will be used as returned value. */ 0564 #define CLEAN_AND_REMOVE(FILENAME) \ 0565 if (FILENAME && (FILENAME != synctex_name)) { \ 0566 remove(FILENAME); \ 0567 printf("synctex tool info: %s removed\n", FILENAME); \ 0568 free(FILENAME); \ 0569 FILENAME = NULL; \ 0570 } 0571 CLEAN_AND_REMOVE(basic_name); 0572 CLEAN_AND_REMOVE(gz_name); 0573 CLEAN_AND_REMOVE(quoted_name); 0574 CLEAN_AND_REMOVE(quoted_gz_name); 0575 CLEAN_AND_REMOVE(build_name); 0576 CLEAN_AND_REMOVE(build_gz_name); 0577 CLEAN_AND_REMOVE(build_quoted_name); 0578 CLEAN_AND_REMOVE(build_quoted_gz_name); 0579 #undef CLEAN_AND_REMOVE 0580 /* set up the returned values */ 0581 *synctex_name_ref = synctex_name; 0582 /* synctex_name won't always end in .gz, even when compressed. */ 0583 FILE *F = fopen(synctex_name, "r"); 0584 if (F != NULL) { 0585 if (!feof(F) && 31 == fgetc(F) && !feof(F) && 139 == fgetc(F)) { 0586 io_mode = synctex_compress_mode_gz; 0587 } 0588 fclose(F); 0589 } 0590 *io_mode_ref = io_mode; 0591 return 0; 0592 } 0593 return -1; /* bad argument */ 0594 } 0595 return -2; 0596 } 0597 0598 const char *_synctex_get_io_mode_name(synctex_io_mode_t io_mode) 0599 { 0600 static const char *synctex_io_modes[4] = {"r", "rb", "a", "ab"}; 0601 unsigned index = ((io_mode & synctex_io_gz_mask) ? 1 : 0) + ((io_mode & synctex_io_append_mask) ? 2 : 0); /* bug pointed out by Jose Alliste */ 0602 return synctex_io_modes[index]; 0603 }