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 }