123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570 |
- /*
- Copyright (c) 2008-2017 jerome DOT laurens AT u-bourgogne DOT fr
-
- This file is part of the __SyncTeX__ package.
-
- [//]: # (Latest Revision: Fri Jul 14 16:20:41 UTC 2017)
- [//]: # (Version: 1.21)
-
- See `synctex_parser_readme.md` for more details
-
- ## License
-
- Permission is hereby granted, free of charge, to any person
- obtaining a copy of this software and associated documentation
- files (the "Software"), to deal in the Software without
- restriction, including without limitation the rights to use,
- copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following
- conditions:
-
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE
-
- Except as contained in this notice, the name of the copyright holder
- shall not be used in advertising or otherwise to promote the sale,
- use or other dealings in this Software without prior written
- authorization from the copyright holder.
- */
- /* In this file, we find all the functions that may depend on the operating system. */
- #include "synctex_parser_utils.h"
- #include <stdlib.h>
- #include <string.h>
- #include <stdarg.h>
- #include <stdio.h>
- #include <limits.h>
- #include <ctype.h>
- #include <string.h>
- #include <sys/stat.h>
- #if defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
- #define SYNCTEX_WINDOWS 1
- #endif
- #if defined(__OS2__)
- #define SYNCTEX_OS2 1
- #endif
- #if defined(_WIN32)
- #define SYNCTEX_RECENT_WINDOWS 1
- #endif
- #ifdef SYNCTEX_WINDOWS
- #include <windows.h>
- #include <shlwapi.h> /* Use shlwapi.lib */
- #endif
- void *_synctex_malloc(size_t size) {
- void * ptr = malloc(size);
- if(ptr) {
- memset(ptr,0, size);/* ensures null termination of strings */
- }
- return (void *)ptr;
- }
- void _synctex_free(void * ptr) {
- if (ptr) {
- free(ptr);
- }
- }
- #if !defined(_WIN32)
- # include <syslog.h>
- #endif
- static int _synctex_log(int level, const char * prompt, const char * reason,va_list arg) {
- int result;
- # ifdef SYNCTEX_RECENT_WINDOWS
- {/* This code is contributed by William Blum.
- As it does not work on some older computers,
- the _WIN32 conditional here is replaced with a SYNCTEX_RECENT_WINDOWS one.
- According to http://msdn.microsoft.com/en-us/library/aa363362(VS.85).aspx
- Minimum supported client Windows 2000 Professional
- Minimum supported server Windows 2000 Server
- People running Windows 2K standard edition will not have OutputDebugStringA.
- JL.*/
- char *buff;
- size_t len;
- OutputDebugStringA(prompt);
- len = _vscprintf(reason, arg) + 1;
- buff = (char*)malloc( len * sizeof(char) );
- result = vsprintf(buff, reason, arg) +strlen(prompt);
- OutputDebugStringA(buff);
- OutputDebugStringA("\n");
- free(buff);
- }
- # elif SYNCTEX_USE_SYSLOG
- char * buffer1 = NULL;
- char * buffer2 = NULL;
- openlog ("SyncTeX", LOG_CONS | LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_LOCAL0);
- if (vasprintf(&buffer1,reason,arg)>=0
- && asprintf(&buffer2,"%s%s",prompt, buffer1)>=0) {
- syslog (level, "%s", buffer2);
- result = (int)strlen(buffer2);
- } else {
- syslog (level, "%s",prompt);
- vsyslog(level,reason,arg);
- result = (int)strlen(prompt);
- }
- free(buffer1);
- free(buffer2);
- closelog();
- # else
- FILE * where = level == LOG_ERR? stderr: stdout;
- result = fputs(prompt,where);
- result += vfprintf(where, reason, arg);
- result += fprintf(where,"\n");
- # endif
- return result;
- }
- int _synctex_error(const char * reason,...) {
- va_list arg;
- int result;
- va_start (arg, reason);
- #if defined(SYNCTEX_RECENT_WINDOWS) /* LOG_ERR is not used */
- result = _synctex_log(0, "! SyncTeX Error : ", reason, arg);
- #else
- result = _synctex_log(LOG_ERR, "! SyncTeX Error : ", reason, arg);
- #endif
- va_end (arg);
- return result;
- }
- int _synctex_debug(const char * reason,...) {
- va_list arg;
- int result;
- va_start (arg, reason);
- #if defined(SYNCTEX_RECENT_WINDOWS) /* LOG_DEBUG is not used */
- result = _synctex_log(0, "! SyncTeX Error : ", reason, arg);
- #else
- result = _synctex_log(LOG_DEBUG, "! SyncTeX Error : ", reason, arg);
- #endif
- va_end (arg);
- return result;
- }
- /* strip the last extension of the given string, this string is modified! */
- void _synctex_strip_last_path_extension(char * string) {
- if(NULL != string){
- char * last_component = NULL;
- char * last_extension = NULL;
- # if defined(SYNCTEX_WINDOWS)
- last_component = PathFindFileName(string);
- last_extension = PathFindExtension(string);
- if(last_extension == NULL)return;
- if(last_component == NULL)last_component = string;
- if(last_extension>last_component){/* filter out paths like "my/dir/.hidden" */
- last_extension[0] = '\0';
- }
- # else
- char * next = NULL;
- /* first we find the last path component */
- if(NULL == (last_component = strstr(string,"/"))){
- last_component = string;
- } else {
- ++last_component;
- while((next = strstr(last_component,"/"))){
- last_component = next+1;
- }
- }
- # if defined(SYNCTEX_OS2)
- /* On OS2, the '\' is also a path separator. */
- while((next = strstr(last_component,"\\"))){
- last_component = next+1;
- }
- # endif /* SYNCTEX_OS2 */
- /* then we find the last path extension */
- if((last_extension = strstr(last_component,"."))){
- ++last_extension;
- while((next = strstr(last_extension,"."))){
- last_extension = next+1;
- }
- --last_extension;/* back to the "." */
- if(last_extension>last_component){/* filter out paths like ....my/dir/.hidden"*/
- last_extension[0] = '\0';
- }
- }
- # endif /* SYNCTEX_WINDOWS */
- }
- }
- synctex_bool_t synctex_ignore_leading_dot_slash_in_path(const char ** name_ref)
- {
- if (SYNCTEX_IS_DOT((*name_ref)[0]) && SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[1])) {
- do {
- (*name_ref) += 2;
- while (SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[0])) {
- ++(*name_ref);
- }
- } while(SYNCTEX_IS_DOT((*name_ref)[0]) && SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[1]));
- return synctex_YES;
- }
- return synctex_NO;
- }
- /* The base name is necessary to deal with the 2011 file naming convention...
- * path is a '\0' terminated string
- * The return value is the trailing part of the argument,
- * just following the first occurrence of the regexp pattern "[^|/|\].[\|/]+".*/
- const char * _synctex_base_name(const char *path) {
- const char * ptr = path;
- do {
- if (synctex_ignore_leading_dot_slash_in_path(&ptr)) {
- return ptr;
- }
- do {
- if (!*(++ptr)) {
- return path;
- }
- } while (!SYNCTEX_IS_PATH_SEPARATOR(*ptr));
- } while (*(++ptr));
- return path;
- }
- /* Compare two file names, windows is sometimes case insensitive... */
- synctex_bool_t _synctex_is_equivalent_file_name(const char *lhs, const char *rhs) {
- /* Remove the leading regex '(\./+)*' in both rhs and lhs */
- synctex_ignore_leading_dot_slash_in_path(&lhs);
- synctex_ignore_leading_dot_slash_in_path(&rhs);
- next_character:
- if (SYNCTEX_IS_PATH_SEPARATOR(*lhs)) {/* lhs points to a path separator */
- if (!SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/* but not rhs */
- return synctex_NO;
- }
- ++lhs;
- ++rhs;
- synctex_ignore_leading_dot_slash_in_path(&lhs);
- synctex_ignore_leading_dot_slash_in_path(&rhs);
- goto next_character;
- } else if (SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/* rhs points to a path separator but not lhs */
- return synctex_NO;
- } else if (SYNCTEX_ARE_PATH_CHARACTERS_EQUAL(*lhs,*rhs)){/* uppercase do not match */
- return synctex_NO;
- } else if (!*lhs) {/* lhs is at the end of the string */
- return *rhs ? synctex_NO : synctex_YES;
- } else if(!*rhs) {/* rhs is at the end of the string but not lhs */
- return synctex_NO;
- }
- ++lhs;
- ++rhs;
- goto next_character;
- }
- synctex_bool_t _synctex_path_is_absolute(const char * name) {
- if(!strlen(name)) {
- return synctex_NO;
- }
- # if defined(SYNCTEX_WINDOWS) || defined(SYNCTEX_OS2)
- if(strlen(name)>2) {
- return (name[1]==':' && SYNCTEX_IS_PATH_SEPARATOR(name[2]))?synctex_YES:synctex_NO;
- }
- return synctex_NO;
- # else
- return SYNCTEX_IS_PATH_SEPARATOR(name[0])?synctex_YES:synctex_NO;
- # endif
- }
- /* We do not take care of UTF-8 */
- const char * _synctex_last_path_component(const char * name) {
- const char * c = name+strlen(name);
- if(c>name) {
- if(!SYNCTEX_IS_PATH_SEPARATOR(*c)) {
- do {
- --c;
- if(SYNCTEX_IS_PATH_SEPARATOR(*c)) {
- return c+1;
- }
- } while(c>name);
- }
- return c;/* the last path component is the void string*/
- }
- return c;
- }
- int _synctex_copy_with_quoting_last_path_component(const char * src, char ** dest_ref, size_t size) {
- if(src && dest_ref) {
- const char * lpc;
- # define dest (*dest_ref)
- dest = NULL; /* Default behavior: no change and sucess. */
- lpc = _synctex_last_path_component(src);
- if(strlen(lpc)) {
- if(strchr(lpc,' ') && lpc[0]!='"' && lpc[strlen(lpc)-1]!='"') {
- /* We are in the situation where adding the quotes is allowed. */
- /* Time to add the quotes. */
- /* Consistency test: we must have dest+size>dest+strlen(dest)+2
- * or equivalently: strlen(dest)+2<size (see below) */
- if(strlen(src)<size) {
- if((dest = (char *)malloc(size+2))) {
- char * dpc = dest + (lpc-src); /* dpc is the last path component of dest. */
- if(dest != strncpy(dest,src,size)) {
- _synctex_error("! _synctex_copy_with_quoting_last_path_component: Copy problem");
- free(dest);
- dest = NULL;/* Don't forget to reinitialize. */
- return -2;
- }
- memmove(dpc+1,dpc,strlen(dpc)+1); /* Also move the null terminating character. */
- dpc[0]='"';
- dpc[strlen(dpc)+1]='\0';/* Consistency test */
- dpc[strlen(dpc)]='"';
- return 0; /* Success. */
- }
- return -1; /* Memory allocation error. */
- }
- _synctex_error("! _synctex_copy_with_quoting_last_path_component: Internal inconsistency");
- return -3;
- }
- return 0; /* Success. */
- }
- return 0; /* No last path component. */
- # undef dest
- }
- return 1; /* Bad parameter, this value is subject to changes. */
- }
- /* The client is responsible of the management of the returned string, if any. */
- char * _synctex_merge_strings(const char * first,...);
- char * _synctex_merge_strings(const char * first,...) {
- va_list arg;
- size_t size = 0;
- const char * temp;
- /* First retrieve the size necessary to store the merged string */
- va_start (arg, first);
- temp = first;
- do {
- size_t len = strlen(temp);
- if(UINT_MAX-len<size) {
- _synctex_error("! _synctex_merge_strings: Capacity exceeded.");
- return NULL;
- }
- size+=len;
- } while( (temp = va_arg(arg, const char *)) != NULL);
- va_end(arg);
- if(size>0) {
- char * result = NULL;
- ++size;
- /* Create the memory storage */
- if(NULL!=(result = (char *)malloc(size))) {
- char * dest = result;
- va_start (arg, first);
- temp = first;
- do {
- if((size = strlen(temp))>0) {
- /* There is something to merge */
- if(dest != strncpy(dest,temp,size)) {
- _synctex_error("! _synctex_merge_strings: Copy problem");
- free(result);
- result = NULL;
- return NULL;
- }
- dest += size;
- }
- } while( (temp = va_arg(arg, const char *)) != NULL);
- va_end(arg);
- dest[0]='\0';/* Terminate the merged string */
- return result;
- }
- _synctex_error("! _synctex_merge_strings: Memory problem");
- return NULL;
- }
- return NULL;
- }
- /* The purpose of _synctex_get_name is to find the name of the synctex file.
- * There is a list of possible filenames from which we return the most recent one and try to remove all the others.
- * With two runs of pdftex or xetex we are sure the the synctex file is really the most appropriate.
- */
- int _synctex_get_name(const char * output, const char * build_directory, char ** synctex_name_ref, synctex_io_mode_t * io_mode_ref)
- {
- if(output && synctex_name_ref && io_mode_ref) {
- /* If output is already absolute, we just have to manage the quotes and the compress mode */
- size_t size = 0;
- char * synctex_name = NULL;
- synctex_io_mode_t io_mode = *io_mode_ref;
- const char * base_name = _synctex_last_path_component(output); /* do not free, output is the owner. base name of output*/
- /* Do we have a real base name ? */
- if(strlen(base_name)>0) {
- /* Yes, we do. */
- const char * temp = NULL;
- char * core_name = NULL; /* base name of output without path extension. */
- char * dir_name = NULL; /* dir name of output */
- char * quoted_core_name = NULL;
- char * basic_name = NULL;
- char * gz_name = NULL;
- char * quoted_name = NULL;
- char * quoted_gz_name = NULL;
- char * build_name = NULL;
- char * build_gz_name = NULL;
- char * build_quoted_name = NULL;
- char * build_quoted_gz_name = NULL;
- struct stat buf;
- time_t the_time = 0;
- /* Create core_name: let temp point to the dot before the path extension of base_name;
- * We start form the \0 terminating character and scan the string upward until we find a dot.
- * The leading dot is not accepted. */
- if((temp = strrchr(base_name,'.')) && (size = temp - base_name)>0) {
- /* There is a dot and it is not at the leading position */
- if(NULL == (core_name = (char *)malloc(size+1))) {
- _synctex_error("! _synctex_get_name: Memory problem 1");
- return -1;
- }
- if(core_name != strncpy(core_name,base_name,size)) {
- _synctex_error("! _synctex_get_name: Copy problem 1");
- free(core_name);
- dir_name = NULL;
- return -2;
- }
- core_name[size] = '\0';
- } else {
- /* There is no path extension,
- * Just make a copy of base_name */
- core_name = _synctex_merge_strings(base_name);
- }
- /* core_name is properly set up, owned by "self". */
- /* creating dir_name. */
- size = strlen(output)-strlen(base_name);
- if(size>0) {
- /* output contains more than one path component */
- if(NULL == (dir_name = (char *)malloc(size+1))) {
- _synctex_error("! _synctex_get_name: Memory problem");
- free(core_name);
- return -1;
- }
- if(dir_name != strncpy(dir_name,output,size)) {
- _synctex_error("! _synctex_get_name: Copy problem");
- free(dir_name);
- dir_name = NULL;
- free(core_name);
- dir_name = NULL;
- return -2;
- }
- dir_name[size] = '\0';
- }
- /* dir_name is properly set up. It ends with a path separator, if non void. */
- /* creating quoted_core_name. */
- if(strchr(core_name,' ')) {
- quoted_core_name = _synctex_merge_strings("\"",core_name,"\"");
- }
- /* quoted_core_name is properly set up. */
- if(dir_name &&strlen(dir_name)>0) {
- basic_name = _synctex_merge_strings(dir_name,core_name,synctex_suffix,NULL);
- if(quoted_core_name && strlen(quoted_core_name)>0) {
- quoted_name = _synctex_merge_strings(dir_name,quoted_core_name,synctex_suffix,NULL);
- }
- } else {
- basic_name = _synctex_merge_strings(core_name,synctex_suffix,NULL);
- if(quoted_core_name && strlen(quoted_core_name)>0) {
- quoted_name = _synctex_merge_strings(quoted_core_name,synctex_suffix,NULL);
- }
- }
- if(!_synctex_path_is_absolute(output) && build_directory && (size = strlen(build_directory))) {
- temp = build_directory + size - 1;
- if(_synctex_path_is_absolute(temp)) {
- build_name = _synctex_merge_strings(build_directory,basic_name,NULL);
- if(quoted_core_name && strlen(quoted_core_name)>0) {
- build_quoted_name = _synctex_merge_strings(build_directory,quoted_name,NULL);
- }
- } else {
- build_name = _synctex_merge_strings(build_directory,"/",basic_name,NULL);
- if(quoted_core_name && strlen(quoted_core_name)>0) {
- build_quoted_name = _synctex_merge_strings(build_directory,"/",quoted_name,NULL);
- }
- }
- }
- if(basic_name) {
- gz_name = _synctex_merge_strings(basic_name,synctex_suffix_gz,NULL);
- }
- if(quoted_name) {
- quoted_gz_name = _synctex_merge_strings(quoted_name,synctex_suffix_gz,NULL);
- }
- if(build_name) {
- build_gz_name = _synctex_merge_strings(build_name,synctex_suffix_gz,NULL);
- }
- if(build_quoted_name) {
- build_quoted_gz_name = _synctex_merge_strings(build_quoted_name,synctex_suffix_gz,NULL);
- }
- /* All the others names are properly set up... */
- /* retain the most recently modified file */
- # define TEST(FILENAME,COMPRESS_MODE) \
- if(FILENAME) {\
- if (stat(FILENAME, &buf)) { \
- free(FILENAME);\
- FILENAME = NULL;\
- } else if (buf.st_mtime>the_time) { \
- the_time=buf.st_mtime; \
- synctex_name = FILENAME; \
- if (COMPRESS_MODE) { \
- io_mode |= synctex_io_gz_mask; \
- } else { \
- io_mode &= ~synctex_io_gz_mask; \
- } \
- } \
- }
- TEST(basic_name,synctex_DONT_COMPRESS);
- TEST(gz_name,synctex_COMPRESS);
- TEST(quoted_name,synctex_DONT_COMPRESS);
- TEST(quoted_gz_name,synctex_COMPRESS);
- TEST(build_name,synctex_DONT_COMPRESS);
- TEST(build_gz_name,synctex_COMPRESS);
- TEST(build_quoted_name,synctex_DONT_COMPRESS);
- TEST(build_quoted_gz_name,synctex_COMPRESS);
- # undef TEST
- /* Free all the intermediate filenames, except the one that will be used as returned value. */
- # define CLEAN_AND_REMOVE(FILENAME) \
- if(FILENAME && (FILENAME!=synctex_name)) {\
- remove(FILENAME);\
- printf("synctex tool info: %s removed\n",FILENAME);\
- free(FILENAME);\
- FILENAME = NULL;\
- }
- CLEAN_AND_REMOVE(basic_name);
- CLEAN_AND_REMOVE(gz_name);
- CLEAN_AND_REMOVE(quoted_name);
- CLEAN_AND_REMOVE(quoted_gz_name);
- CLEAN_AND_REMOVE(build_name);
- CLEAN_AND_REMOVE(build_gz_name);
- CLEAN_AND_REMOVE(build_quoted_name);
- CLEAN_AND_REMOVE(build_quoted_gz_name);
- # undef CLEAN_AND_REMOVE
- /* set up the returned values */
- * synctex_name_ref = synctex_name;
- /* synctex_name won't always end in .gz, even when compressed. */
- FILE * F = fopen(synctex_name, "r");
- if (F != NULL) {
- if (!feof(F)
- && 31 == fgetc(F)
- && !feof(F)
- && 139 == fgetc(F)) {
- io_mode = synctex_compress_mode_gz;
- }
- fclose(F);
- }
- * io_mode_ref = io_mode;
- return 0;
- }
- return -1;/* bad argument */
- }
- return -2;
- }
- const char * _synctex_get_io_mode_name(synctex_io_mode_t io_mode) {
- static const char * synctex_io_modes[4] = {"r","rb","a","ab"};
- unsigned index = ((io_mode & synctex_io_gz_mask)?1:0) + ((io_mode & synctex_io_append_mask)?2:0);// bug pointed out by Jose Alliste
- return synctex_io_modes[index];
- }
|