/*
* ChkTeX, operating system specific code for ChkTeX.
* Copyright (C) 1995-96 Jens T. Berger Thielemann
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contact the author at:
* Jens Berger
* Spektrumvn. 4
* N-0666 Oslo
* Norway
* E-mail: <
[email protected]>
*
*
*/
/*
* Some functions which have to be made different from OS to OS,
* unfortunately...:\
*
*/
#include "ChkTeX.h"
#include "OpSys.h"
#include "Utility.h"
#ifdef KPATHSEA
#include <kpathsea/variable.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef HAVE_STAT_H
# include <stat.h>
#endif
#if HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#if defined(HAVE_OPENDIR) && defined(HAVE_CLOSEDIR) && \
defined(HAVE_READDIR) && defined(HAVE_STAT) && \
defined(S_IFDIR) && defined(SLASH)
# define USE_RECURSE 1
#else
# define USE_RECURSE 0
#endif
#if defined(HAVE_LIBTERMCAP) || defined(HAVE_LIBTERMLIB)
# define USE_TERMCAP 1
#endif
#ifdef USE_TERMCAP
# ifdef HAVE_TERMCAP_H
# include <termcap.h>
# elif HAVE_TERMLIB_H
# include <termlib.h>
# else
int tgetent(char *BUFFER, char *TERMTYPE);
char *tgetstr(char *NAME, char **AREA);
# endif
static char term_buffer[2048];
#endif
/* -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- */
/*
* This is the name of the global resource file.
*/
#ifndef SYSCONFDIR
# if defined(__unix__)
# define SYSCONFDIR "/usr/local/lib/"
# elif defined(__MSDOS__)
# define SYSCONFDIR "\\emtex\\data\\"
# else
# define SYSCONFDIR
# endif
#endif
#define RCBASENAME "chktexrc"
#ifdef __MSDOS__
# define LOCALRCFILE RCBASENAME
#elif defined(WIN32)
# define LOCALRCFILE RCBASENAME
#else
# define LOCALRCFILE "." RCBASENAME
#endif
char ConfigFile[BUFFER_SIZE] = LOCALRCFILE;
struct WordList ConfigFiles;
const char *ReverseOn;
const char *ReverseOff;
static int HasFile(char *Dir, const char *Filename, const char *App);
#if USE_RECURSE
static int SearchFile(char *Dir, const char *Filename, const char *App);
#endif /* USE_RECURSE */
/* -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- */
/*
* Modify this one to suit your needs. In any case, it should fill
* the ConfigFile (sized BUFLEN) buffer above with full name & path
* for the configuration file. The macro RCFILE will give you the
* filename part of the file, if you need that.
*
* Note: This routine will be called several times. Your mission will
* be to look in each location, and see whether a .chktexrc file exist
* there.
*
* If you choose to do nothing, only the current directory will be
* searched.
*
*/
enum LookIn
{
liMin,
liSysDir,
liUsrDir,
liXdgDir,
liEnvir,
liCurDir,
liNFound,
liMax
};
int SetupVars(void)
{
char *Env;
#ifdef __MSDOS__
char *Ptr;
#endif
static enum LookIn i = liMin;
static int FoundFile;
while (++i < liMax)
{
switch (i)
{
case liCurDir: /* Current directory */
strcpy(ConfigFile, LOCALRCFILE);
break;
case liEnvir: /* Environment defined */
#ifdef __MSDOS__
if ((Env = getenv("CHKTEXRC")) || (Env = getenv("CHKTEX_HOME")))
#elif defined(TEX_LIVE)
if ((Env = kpse_var_value("CHKTEXRC")))
#else
if ((Env = getenv("CHKTEXRC")))
#endif
{
strcpy(ConfigFile, Env);
tackon(ConfigFile, LOCALRCFILE);
#ifdef TEX_LIVE
free(Env);
#endif
}
else
#ifdef __MSDOS__
if ((Env = getenv("EMTEXDIR")))
{
strcpy(ConfigFile, Env);
tackon(ConfigFile, "data");
tackon(ConfigFile, LOCALRCFILE);
}
else
#endif
*ConfigFile = 0;
break;
case liXdgDir: /* Cross-desktop group dir for resource files */
/* XDG is really unix specific, but it shouldn't hurt to
* support it on Windows, should someone set the variables. */
if ((Env = getenv("XDG_CONFIG_HOME")) && *Env)
{
strcpy(ConfigFile, Env);
tackon(ConfigFile, RCBASENAME);
}
else if ((Env = getenv("HOME")) && *Env)
{
strcpy(ConfigFile, Env);
tackon(ConfigFile, ".config");
tackon(ConfigFile, RCBASENAME);
}
else
*ConfigFile = 0;
break;
case liUsrDir: /* User dir for resource files */
#if defined(__unix__)
if ((Env = getenv("HOME")) || (Env = getenv("LOGDIR")))
{
strcpy(ConfigFile, Env);
tackon(ConfigFile, LOCALRCFILE);
}
else
#elif defined(__MSDOS__)
strcpy(ConfigFile, PrgName);
if ((Ptr = strrchr(ConfigFile, '\\')) ||
(Ptr = strchr(ConfigFile, ':')))
strcpy(++Ptr, RCBASENAME);
else
#endif
*ConfigFile = 0;
break;
case liSysDir: /* System dir for resource files */
#ifdef TEX_LIVE
if ((Env = kpse_var_value("CHKTEX_CONFIG")))
{
strcpy(ConfigFile, Env);
free(Env);
}
else if ((Env = kpse_var_value("TEXMFMAIN")))
{
strcpy(ConfigFile, Env);
tackon(ConfigFile, "chktex");
tackon(ConfigFile, RCBASENAME);
free(Env);
}
else
*ConfigFile = 0;
#else /* TEX_LIVE */
#if defined(__unix__) || defined(__MSDOS__)
strcpy(ConfigFile, SYSCONFDIR);
tackon(ConfigFile, RCBASENAME);
#else
*ConfigFile = 0;
#endif
#endif /* TEX_LIVE */
break;
case liNFound:
case liMin:
case liMax:
*ConfigFile = 0;
if (!FoundFile)
PrintPrgErr(pmNoRsrc);
}
if (*ConfigFile && fexists(ConfigFile))
break;
}
FoundFile |= *ConfigFile;
return (*ConfigFile);
}
/* -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- */
/*
* This function should initialize the global variables ReverseOn
* and ReverseOff to magic cookies, which when printed, makes the
* text in between stand out.
*/
void SetupTerm(void)
{
#ifdef USE_TERMCAP
char *termtype = getenv("TERM");
int success;
char *buffer;
static char str_so[3] = "so", str_se[3] = "se";
if (termtype)
{
success = tgetent(term_buffer, termtype);
if (success < 0)
PrintPrgErr(pmNoTermData);
if (success == 0)
PrintPrgErr(pmNoTermDefd);
buffer = (char *) malloc(sizeof(term_buffer));
ReverseOn = tgetstr(str_so, &buffer);
ReverseOff = tgetstr(str_se, &buffer);
if (ReverseOn && ReverseOff)
return;
}
#endif
ReverseOn = PRE_ERROR_STR;
ReverseOff = POST_ERROR_STR;
}
/* -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- */
/*
* Concatenates the `File' string to the `Dir' string, leaving the result
* in the `Dir' buffer. Takes care of inserting `directory' characters;
* if we've got the strings "/usr/foo" and "bar", we'll get
* "/usr/foo/bar".
*
* Behaviour somewhat controlled by the macros SLASH and DIRCHARS in the
* OpSys.h file.
*
*/
void tackon(char *Dir, const char *File)
{
int EndC;
unsigned long SLen;
if (Dir && (SLen = strlen(Dir)))
{
EndC = Dir[SLen - 1];
if (!(strchr(DIRCHARS, EndC)))
{
Dir[SLen++] = SLASH;
Dir[SLen] = 0L;
}
}
strcat(Dir, File);
}
/*
* This function should add the appendix App to the filename Name.
* If the resulting filename gets too long due to this, it may
* overwrite the old appendix.
*
* Name may be assumed to be a legal filename under your OS.
*
* The appendix should contain a leading dot.
*/
void AddAppendix(char *Name, const char *App)
{
#ifdef __MSDOS__
char *p;
if ((p = strrchr(Name, '.')))
strcpy(p, App);
else
strcat(Name, App);
#else
/*
* NOTE! This may fail if your system has a claustrophobic file
* name length limit.
*/
strcat(Name, App);
#endif
}
/* -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- */
/*
* Locates a file, given a wordlist containing paths. If a
* dir ends in a double SLASH, we'll search it recursively.
*
* We assume that
* a) a deeper level in the dir. tree. has a longer path than
* one above.
* b) adding a level doesn't change any of the previous levels.
*
* If this function returns TRUE, Dest is guaranteed to contain
* path & name of the found file.
*
* FALSE indicates that the file was not found; Dest is then
* unspecified.
*/
int LocateFile(const char *Filename, /* File to search for */
char *Dest, /* Where to put final file */
const char *App, /* Extra optional appendix */
struct WordList *wl) /* List of paths, entries
* ending in // will be recursed
*/
{
unsigned long i;
#if USE_RECURSE
unsigned long Len;
#endif
FORWL(i, *wl)
{
strcpy(Dest, wl->Stack.Data[i]);
#if USE_RECURSE
Len = strlen(Dest);
if (Len && (Dest[Len - 1] == SLASH) && (Dest[Len - 2] == SLASH))
{
Dest[Len - 1] = Dest[Len - 2] = 0;
if (SearchFile(Dest, Filename, App))
return (TRUE);
}
else
#endif /* USE_RECURSE */
{
if (HasFile(Dest, Filename, App))
return (TRUE);
}
}
return (FALSE);
}
static int HasFile(char *Dir, const char *Filename, const char *App)
{
int DirLen = strlen(Dir);
tackon(Dir, Filename);
if (fexists(Dir))
return (TRUE);
if (App)
{
AddAppendix(Dir, App);
if (fexists(Dir))
return (TRUE);
}
Dir[DirLen] = 0;
return (FALSE);
}
/*
* If Filename is contains a directory component, then add a fully qualified
* directory to the TeXInputs WordList.
*
* I'm not sure how it will work with some Windows paths,
* e.g. C:path\to\file.tex since it doesn't really understand the leading C:
* But I'm not sure it would even work with a path like that, and I have no
* way to test it.
*
* Behaviour somewhat controlled by the macros SLASH and DIRCHARS in the
* OpSys.h file.
*
*/
void AddDirectoryFromRelativeFile(const char * Filename, struct WordList *TeXInputs)
{
if ( ! Filename )
return;
/* There are no path delimiters, so it's just a file, return null */
if ( ! strstr( Filename, DIRCHARS ) )
return;
char buf[BUFFER_SIZE];
if ( strchr(DIRCHARS,Filename[0]) ) {
strcpy(buf,Filename);
} else {
getcwd(buf, BUFFER_SIZE);
tackon(buf,Filename);
}
/* Keep up to the final SLASH -- that will be the directory. */
char * end = strrchr(buf,SLASH);
*end = '\0';
InsertWord(buf,TeXInputs);
}
#if USE_RECURSE
static int SearchFile(char *Dir, const char *Filename, const char *App)
{
struct stat *statbuf;
struct dirent *de;
DIR *dh;
int DirLen = strlen(Dir);
int Found = FALSE;
DEBUG(("Searching %s for %s\n", Dir, Filename));
if (HasFile(Dir, Filename, App))
return (TRUE);
else
{
if ((statbuf = malloc(sizeof(struct stat))))
{
if ((dh = opendir(Dir)))
{
while (!Found && (de = readdir(dh)))
{
Dir[DirLen] = 0;
if (strcmp(de->d_name, ".") && strcmp(de->d_name, ".."))
{
tackon(Dir, de->d_name);
if (!stat(Dir, statbuf))
{
if ((statbuf->st_mode & S_IFMT) == S_IFDIR)
Found = SearchFile(Dir, Filename, App);
}
}
}
closedir(dh);
}
else
PrintPrgErr(pmNoOpenDir, Dir);
free(statbuf);
}
}
return (Found);
}
#endif /* USE_RECURSE */
#if defined(HAVE_STAT)
int IsRegFile(const char *Filename)
{
int Retval = FALSE;
struct stat *statbuf;
if ((statbuf = malloc(sizeof(struct stat))))
{
if (!stat(Filename, statbuf))
{
if ((statbuf->st_mode & S_IFMT) == S_IFREG)
Retval = TRUE;
}
free(statbuf);
}
return Retval;
}
#else
int IsRegFile(const char *Filename) { printf("WTF\n");return TRUE; }
#endif