/* knj.c: check for 2-Byte Kanji (CP 932, SJIS) codes.
Copyright 2010, 2016, 2018 Akira Kakuto.
Copyright 2013, 2016, 2020 TANAKA Takuji.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, see . */
#include
#include
#include
static int
is_include_space(const char *s)
{
char *p;
p = strchr(s, ' ');
if(p) return 1;
p = strchr(s, '\t');
if(p) return 1;
return 0;
}
/*
Get wide string from multibyte string.
*/
wchar_t *
get_wstring_from_mbstring(int cp, const char *mbstr, wchar_t *wstr)
{
int len;
len = MultiByteToWideChar(cp, 0, mbstr, -1, wstr, 0);
if (len==0) {
FATAL("cannot convert string to wide string");
}
if (wstr==NULL) {
wstr = xmalloc(sizeof(wchar_t)*(len+1));
}
len = MultiByteToWideChar(cp, 0, mbstr, -1, wstr, len+1);
if (len==0) {
FATAL("cannot convert multibyte string to wide string");
}
return wstr;
}
/*
Get multibyte string from wide string.
*/
char *
get_mbstring_from_wstring(int cp, const wchar_t *wstr, char *mbstr)
{
int len;
len = WideCharToMultiByte(cp, 0, wstr, -1, mbstr, 0, NULL, NULL);
if (len==0) {
FATAL("cannot convert string to multibyte string");
}
if (mbstr==NULL) {
mbstr = xmalloc(len+1);
}
len = WideCharToMultiByte(cp, 0, wstr, -1, mbstr, len+1, NULL, NULL);
if (len==0) {
FATAL("cannot convert wide string to multibyte string");
}
return mbstr;
}
int
kpathsea_isknj(kpathsea kpse, int c)
{
c &= 0xff;
switch (kpse->Is_cp932_system) {
case 932:
return((c>=0x81 && c<=0x9f) || (c>=0xe0 && c<=0xfc));
case 936:
return(c>=0x81 && c<=0xfe);
case 950:
return((c>=0xa1 && c<=0xc6) || (c>=0xc9 && c<=0xf9));
default:
return(0);
}
}
int
kpathsea_isknj2(kpathsea kpse, int c)
{
c &= 0xff;
switch (kpse->Is_cp932_system) {
case 932:
return(c>=0x40 && c<=0xfc && c!=0x7f);
case 936:
return(c>=0x40 && c<=0xfe && c!=0x7f);
case 950:
return((c>=0x40 && c<=0x7e) || (c>=0xa1 && c<=0xfe));
default:
return(0);
}
}
/*
xfopen by file system codepage
*/
FILE *
kpathsea_fsyscp_xfopen (kpathsea kpse, const char *filename, const char *mode)
{
FILE *f;
wchar_t *fnamew, modew[4];
int i;
unsigned char *fnn;
unsigned char *p = NULL;
size_t len;
assert(filename && mode);
len = strlen(filename);
/*
Support very long input path name, longer than _MAX_PATH for
Windows, if it really exists and input name is given in
full-absolute path in a command line.
/./ , /../, \.\, \..\ should be excluded. (2020/06/06)
More than one adjacent directory separators should be
excluded. (2020/10/24)
The "nul" device should be excluded. (2021/04/07).
*/
fnn = xmalloc(len + 10);
if (stricmp(filename + len - 3, "nul") == 0)
p = (unsigned char *)filename;
else if (stricmp(filename + len - 4, "nul:") == 0)
p = (unsigned char *)filename;
else if (stricmp(filename + len - 7, "nul.tex") == 0)
p = (unsigned char *)filename;
else if (stricmp(filename + len - 8, "nul:.tex") == 0)
p = (unsigned char *)filename;
if (!p) {
p = strstr(filename, ".\\");
}
if (!p) {
p = strstr(filename, "./");
}
if (!p && len > 2) {
p = strstr(filename + 2, "//");
}
if (!p && len > 2) {
p = strstr(filename + 2, "\\\\");
}
if (!p && len > 2) {
p = strstr(filename + 2, "\\/");
}
if (!p && len > 2) {
p = strstr(filename + 2, "/\\");
}
if (!p && len > 2 && ((filename[0] == '/' && filename[1] == '/') ||
(filename[0] == '\\' && filename[1] == '\\' &&
filename[2] != '?'))) {
filename += 2;
strcpy (fnn, "\\\\?\\UNC\\");
strcat (fnn, filename);
} else if (!p && len > 2 && filename[1] == ':') {
strcpy (fnn, "\\\\?\\");
strcat (fnn, filename);
} else {
strcpy (fnn, filename);
}
for (p = fnn; *p; p++) {
if (*p == '/')
*p = '\\';
}
fnamew = get_wstring_from_mbstring(kpse->File_system_codepage, fnn, fnamew=NULL);
for(i=0; (modew[i]=(wchar_t)mode[i]); i++) {} /* mode[i] must be ASCII */
f = _wfopen(fnamew, modew);
free (fnn);
if (f == NULL)
FATAL_PERROR(filename);
if (KPATHSEA_DEBUG_P (KPSE_DEBUG_FOPEN)) {
DEBUGF_START ();
fprintf (stderr, "fsyscp_xfopen(%s [", filename);
WriteConsoleW( GetStdHandle( STD_ERROR_HANDLE ), fnamew, wcslen( fnamew ), NULL, NULL );
#if defined(_WIN64)
fprintf (stderr, "], %s) => 0x%I64x\n", mode, (unsigned __int64) f);
#else
fprintf (stderr, "], %s) => 0x%lx\n", mode, (unsigned long) f);
#endif
DEBUGF_END ();
}
free(fnamew);
return f;
}
/*
fopen by file system codepage
*/
FILE *
kpathsea_fsyscp_fopen (kpathsea kpse, const char *filename, const char *mode)
{
FILE *f;
wchar_t *fnamew, modew[4];
int i;
unsigned char *fnn;
unsigned char *p = NULL;
size_t len;
assert(filename && mode);
len = strlen(filename);
/*
Support very long input path name, longer than _MAX_PATH for
Windows, if it really exists and input name is given in
full-absolute path in a command line.
/./ , /../, \.\, \..\ should be excluded. (2020/06/06)
More than one adjacent directory separators should be
excluded. (2020/10/24)
The "nul" device should be excluded. (2021/04/07).
*/
fnn = xmalloc(len + 10);
if (stricmp(filename + len - 3, "nul") == 0)
p = (unsigned char *)filename;
else if (stricmp(filename + len - 4, "nul:") == 0)
p = (unsigned char *)filename;
else if (stricmp(filename + len - 7, "nul.tex") == 0)
p = (unsigned char *)filename;
else if (stricmp(filename + len - 8, "nul:.tex") == 0)
p = (unsigned char *)filename;
if (!p) {
p = strstr(filename, ".\\");
}
if (!p) {
p = strstr(filename, "./");
}
if (!p && len > 2) {
p = strstr(filename + 2, "//");
}
if (!p && len > 2) {
p = strstr(filename + 2, "\\\\");
}
if (!p && len > 2) {
p = strstr(filename + 2, "\\/");
}
if (!p && len > 2) {
p = strstr(filename + 2, "/\\");
}
if (!p && len > 2 && ((filename[0] == '/' && filename[1] == '/') ||
(filename[0] == '\\' && filename[1] == '\\' &&
filename[2] != '?'))) {
filename += 2;
strcpy (fnn, "\\\\?\\UNC\\");
strcat (fnn, filename);
} else if (!p && len > 2 && filename[1] == ':') {
strcpy (fnn, "\\\\?\\");
strcat (fnn, filename);
} else {
strcpy (fnn, filename);
}
for (p = fnn; *p; p++) {
if (*p == '/')
*p = '\\';
}
fnamew = get_wstring_from_mbstring(kpse->File_system_codepage, fnn, fnamew=NULL);
for(i=0; (modew[i]=(wchar_t)mode[i]); i++) {} /* mode[i] must be ASCII */
f = _wfopen(fnamew, modew);
free (fnn);
if (f != NULL) {
if (KPATHSEA_DEBUG_P (KPSE_DEBUG_FOPEN)) {
DEBUGF_START ();
fprintf (stderr, "fsyscp_fopen(%s [", filename);
WriteConsoleW( GetStdHandle( STD_ERROR_HANDLE ), fnamew, wcslen( fnamew ), NULL, NULL );
#if defined(_WIN64)
fprintf (stderr, "], %s) => 0x%I64x\n", mode, (unsigned __int64) f);
#else
fprintf (stderr, "], %s) => 0x%lx\n", mode, (unsigned long) f);
#endif
DEBUGF_END ();
}
}
free(fnamew);
return f;
}
FILE *
kpathsea_fsyscp_popen (kpathsea kpse, const char *command, const char *mode)
{
FILE *f;
wchar_t *commandw, modew[4];
int i;
assert(command && mode);
if (is_include_space (command)) {
const char *p;
char *command2, *q;
command2 = xmalloc (strlen (command) + 3);
p = command;
q = command2;
*q++ = '\"';
while (*p)
*q++ = *p++;
*q++ = '\"';
*q = '\0';
commandw = get_wstring_from_mbstring(kpse->File_system_codepage,command2, commandw=NULL);
free (command2);
} else {
commandw = get_wstring_from_mbstring(kpse->File_system_codepage,command, commandw=NULL);
}
for(i=0; (modew[i]=(wchar_t)mode[i]); i++) {} /* mode[i] must be ASCII */
f = _wpopen(commandw, modew);
if (f != NULL) {
if (KPATHSEA_DEBUG_P (KPSE_DEBUG_FOPEN)) {
DEBUGF_START ();
fprintf (stderr, "fsyscp_popen(%s [", command);
WriteConsoleW( GetStdHandle( STD_ERROR_HANDLE ), commandw, wcslen( commandw ), NULL, NULL );
#if defined(_WIN64)
fprintf (stderr, "], %s) => 0x%I64x\n", mode, (unsigned __int64) f);
#else
fprintf (stderr, "], %s) => 0x%lx\n", mode, (unsigned long) f);
#endif
DEBUGF_END ();
}
}
free (commandw);
/* We use always binary mode on Windows */
if(f) _setmode (fileno (f), _O_BINARY);
return f;
}
int
kpathsea_get_command_line_args_utf8 (kpathsea kpse, const_string enc, int *p_ac, char ***p_av)
{
int argc;
string *argv;
if (!enc || !strncmp(enc,"",1)) return 0;
#ifdef DEBUG
fprintf(stderr, "command_line_encoding (%s)\n", enc);
#endif /* DEBUG */
if (!(strncmp(enc,"utf8",5) && strncmp(enc,"utf-8",6))) {
LPWSTR *argvw;
INT argcw, i;
string s;
#ifdef DEBUG
DWORD ret;
HANDLE hStderr;
hStderr = GetStdHandle( STD_ERROR_HANDLE );
#endif /* DEBUG */
kpse->File_system_codepage = CP_UTF8;
/*
IS_KANJI() in the CP932-like system seems to be necessary to
support non-ascii values for variables in the case of
command_line_encoding = utf-8.
kpse->Is_cp932_system = 0;
*/
argvw = CommandLineToArgvW(GetCommandLineW(), &argcw);
argc = argcw;
argv = xmalloc(sizeof(char *)*(argcw+1));
for (i=0; iFile_system_codepage;
} else {
return 0;
}
}
/*
spawnvp by file system codepage
*/
int
kpathsea_fsyscp_spawnvp (kpathsea kpse, int mode, const char *command, const char* const *argv)
{
int ret;
wchar_t *commandw, **argvw, **pw;
int i;
const char* const *p;
assert(command && argv);
for (i = 0, p = argv; *p; p++)
i++;
argvw = xcalloc (i + 3, sizeof (wchar_t *));
commandw = get_wstring_from_mbstring(kpse->File_system_codepage, command, commandw=NULL);
p = argv;
pw = argvw;
while (*p) {
*pw = get_wstring_from_mbstring(kpse->File_system_codepage, *p, *pw=NULL);
p++;
pw++;
}
*pw = NULL;
ret = _wspawnvp (mode, (const wchar_t *)commandw, (const wchar_t* const*) argvw);
if(commandw) free(commandw);
if (argvw) {
pw = argvw;
while (*pw) {
free (*pw);
pw++;
}
free (argvw);
}
return ret;
}
/*
system by file system codepage
*/
int
kpathsea_fsyscp_system (kpathsea kpse, const char *cmd)
{
const char *p;
char *q;
char *av[4];
int len, ret;
int spacep = 0;
if (cmd == NULL)
return 1;
av[0] = xstrdup ("cmd.exe");
av[1] = xstrdup ("/c");
len = strlen (cmd) + 3;
spacep = is_include_space (cmd);
av[2] = xmalloc (len);
q = av[2];
if (spacep)
*q++ = '"';
for (p = cmd; *p; p++, q++) {
if (*p == '\'')
*q = '"';
else
*q = *p;
}
if (spacep)
*q++ = '"';
*q = '\0';
av[3] = NULL;
ret = kpathsea_fsyscp_spawnvp (kpse, _P_WAIT, av[0], (const char* const*) av);
free (av[0]);
free (av[1]);
free (av[2]);
return ret;
}
int
kpathsea_win32_getc(kpathsea kpse, FILE *fp)
{
const int fd = fileno(fp);
HANDLE hStdin;
DWORD ret;
wchar_t wc[3];
char mbc[5];
int j;
if (!(fd == fileno(stdin) && _isatty(fd) && kpse->File_system_codepage == CP_UTF8))
return getc(fp);
if (kpse->getc_len == 0)
{
hStdin = GetStdHandle(STD_INPUT_HANDLE);
if (kpse->wcbuf) {
wc[0] = kpse->wcbuf;
kpse->wcbuf = L'\0';
}
else if (ReadConsoleW(hStdin, wc, 1, &ret, NULL) == 0)
return EOF;
if (0xd800<=wc[0] && wc[0]<0xdc00) {
if (ReadConsoleW(hStdin, wc+1, 1, &ret, NULL) == 0)
return EOF;
if (0xdc00<=wc[1] && wc[1]<0xe000) {
wc[2]=L'\0';
} else {
kpse->wcbuf=wc[1];
wc[0]=0xfffd; /* illegal surrogate pair */
wc[1]=L'\0';
}
} else if (0xdc00<=wc[0] && wc[0]<0xe000) {
wc[0]=0xfffd; /* illegal surrogate pair */
wc[1]=L'\0';
} else {
wc[1]=L'\0';
}
get_utf8_from_wstring(wc,mbc);
j=strlen(mbc)-1;
while(j>=0) {
kpse->getc_buff[kpse->getc_len++]=(int)mbc[j--];
}
}
return kpse->getc_buff[--kpse->getc_len];
}
int
kpathsea_win32_ungetc(kpathsea kpse, int c, FILE *fp)
{
const int fd = fileno(fp);
if (!(fd == fileno(stdin) && _isatty(fd) && kpse->File_system_codepage == CP_UTF8))
return ungetc(c, fp);
assert(kpse->getc_len < 4);
return kpse->getc_buff[kpse->getc_len++] = c;
}
static int __win32_fputs(const char *str, HANDLE hStdout)
{
DWORD ret;
wchar_t *wstr;
wstr = get_wstring_from_utf8(str, wstr=NULL);
if (WriteConsoleW(hStdout, wstr, wcslen(wstr), &ret, NULL) == 0) {
free(wstr);
return EOF;
}
free(wstr);
return ret;
}
int
kpathsea_win32_fputs(kpathsea kpse, const char *str, FILE *fp)
{
const int fd = fileno(fp);
HANDLE hStdout;
if (!((fd == fileno(stdout) || fd == fileno(stderr)) && _isatty(fd)
&& kpse->File_system_codepage == CP_UTF8))
return fputs(str, fp);
hStdout = (fd == fileno(stdout)) ?
GetStdHandle(STD_OUTPUT_HANDLE) : GetStdHandle(STD_ERROR_HANDLE);
return __win32_fputs(str, hStdout);
}
#define MAX_PROMPT_STR_SIZE 8192
int
kpathsea_win32_vfprintf(kpathsea kpse, FILE *fp, const char *format, va_list argp)
{
const int fd = fileno(fp);
HANDLE hStdout;
char buff[MAX_PROMPT_STR_SIZE];
int ret;
if (!((fd == fileno(stdout) || fd == fileno(stderr)) && _isatty(fd)
&& kpse->File_system_codepage == CP_UTF8))
return vfprintf(fp, format, argp);
hStdout = (fd == fileno(stdout)) ?
GetStdHandle(STD_OUTPUT_HANDLE) : GetStdHandle(STD_ERROR_HANDLE);
ret = _vsnprintf(buff, sizeof(buff), format, argp);
if (__win32_fputs(buff, hStdout)==EOF) {
return EOF;
}
return ret;
}
int
kpathsea_win32_fprintf(kpathsea kpse, FILE *fp, const char *format, ...)
{
int ret, count;
va_list argp;
count = 0;
va_start(argp, format);
ret = kpathsea_win32_vfprintf(kpse, fp, format, argp);
if (ret==EOF) {
return EOF;
}
count += ret;
va_end(argp);
return count;
}
int
kpathsea_win32_puts(kpathsea kpse, const char *str)
{
if (kpathsea_win32_fputs(kpse, str, stdout)==EOF) {
return EOF;
}
return puts("");
}
int
kpathsea_win32_putc(kpathsea kpse, int c, FILE *fp)
{
const int fd = fileno(fp);
HANDLE hStdout;
DWORD ret;
wchar_t wstr[3];
if (!((fd == fileno(stdout) || fd == fileno(stderr)) && _isatty(fd)
&& kpse->File_system_codepage == CP_UTF8))
return putc(c, fp);
hStdout = (fd == fileno(stdout)) ?
GetStdHandle(STD_OUTPUT_HANDLE) : GetStdHandle(STD_ERROR_HANDLE);
c &= 0xff;
if (c < 0x80) {
kpse->st_str = kpse->st_buff;
kpse->st_len = 1;
}
if (c < 0xc0) { /* ASCII or trailer */
*(kpse->st_str)++ = c;
kpse->st_len--;
if (kpse->st_len == 0) {
*(kpse->st_str) = '\0';
get_wstring_from_utf8(kpse->st_buff, wstr);
if (WriteConsoleW(hStdout, wstr, wcslen(wstr), &ret, NULL) == 0) {
kpse->st_len = 0;
return EOF;
}
}
else if (kpse->st_len < 0) return EOF;
return c;
}
else if (c < 0xc2) { kpse->st_len = 0; return EOF; } /* illegal */
else if (c < 0xe0) kpse->st_len = 2;
else if (c < 0xf0) kpse->st_len = 3;
else if (c < 0xf5) kpse->st_len = 4;
else { kpse->st_len = 0; return EOF; }
kpse->st_str = kpse->st_buff;
*(kpse->st_str)++ = c;
kpse->st_len--;
return c;
}
void
kpathsea_win32_perror(kpathsea kpse, const char *str)
{
wchar_t *wstr;
if (kpse->File_system_codepage != CP_UTF8) {
perror(str);
return;
}
wstr = get_wstring_from_utf8(str, wstr=NULL);
_wperror(wstr);
free(wstr);
}
int
kpathsea_IS_KANJI(kpathsea kpse, const char *p)
{
int ret;
ret = kpse->Is_cp932_system && kpathsea_isknj(kpse, *(p)) &&
kpathsea_isknj2(kpse, *(p+1));
return ret;
}
char *
kpathsea_get_fsyscp_from_wstring(kpathsea kpse, const wchar_t *w,char *mb)
{
return get_mbstring_from_wstring(kpse->File_system_codepage, w, mb);
}
wchar_t *
kpathsea_get_wstring_from_fsyscp(kpathsea kpse, const char *mb,wchar_t *w)
{
return get_wstring_from_mbstring(kpse->File_system_codepage, mb, w);
}
#if defined (KPSE_COMPAT_API)
int
isknj(int c)
{
return kpathsea_isknj(kpse_def, c);
}
int
isknj2(int c)
{
return kpathsea_isknj2(kpse_def, c);
}
FILE *
fsyscp_xfopen (const char *filename, const char *mode)
{
return kpathsea_fsyscp_xfopen (kpse_def, filename, mode);
}
FILE *
fsyscp_fopen (const char *filename, const char *mode)
{
return kpathsea_fsyscp_fopen (kpse_def, filename, mode);
}
FILE *
fsyscp_popen (const char *command, const char *mode)
{
return kpathsea_fsyscp_popen (kpse_def, command, mode);
}
int
get_command_line_args_utf8 (const_string enc, int *p_ac, char ***p_av)
{
return kpathsea_get_command_line_args_utf8 (kpse_def, enc, p_ac, p_av);
}
int
fsyscp_spawnvp (int mode, const char *command, const char* const *argv)
{
return kpathsea_fsyscp_spawnvp (kpse_def, mode, command, argv);
}
int
fsyscp_system (const char *cmd)
{
return kpathsea_fsyscp_system (kpse_def, cmd);
}
int
win32_getc(FILE *fp)
{
return kpathsea_win32_getc(kpse_def, fp);
}
int
win32_ungetc(int c, FILE *fp)
{
return kpathsea_win32_ungetc(kpse_def, c, fp);
}
int
win32_fputs(const char *str, FILE *fp)
{
return kpathsea_win32_fputs(kpse_def, str, fp);
}
int
win32_vfprintf(FILE *fp, const char *format, va_list argp)
{
return kpathsea_win32_vfprintf(kpse_def, fp, format, argp);
}
int
win32_fprintf(FILE *fp, const char *format, ...)
{
int ret, count;
va_list argp;
count = 0;
va_start(argp, format);
ret = kpathsea_win32_vfprintf(kpse_def, fp, format, argp);
if (ret==EOF) {
return EOF;
}
count += ret;
va_end(argp);
return count;
}
int
win32_puts(const char *str)
{
return kpathsea_win32_puts(kpse_def, str);
}
int
win32_putc(int c, FILE *fp)
{
return kpathsea_win32_putc(kpse_def, c, fp);
}
void
win32_perror(const char *str)
{
kpathsea_win32_perror(kpse_def, str);
}
int
IS_KANJI(const char *p)
{
return kpathsea_IS_KANJI(kpse_def, p);
}
char *
get_fsyscp_from_wstring(const wchar_t *w,char *mb)
{
return kpathsea_get_fsyscp_from_wstring(kpse_def, w, mb);
}
wchar_t *
get_wstring_from_fsyscp(const char *mb,wchar_t *w)
{
return kpathsea_get_wstring_from_fsyscp(kpse_def, mb, w);
}
#endif