mirror of
https://github.com/mtheall/ftpd.git
synced 2024-11-26 19:20:31 +00:00
Add glob
This commit is contained in:
parent
663c02172e
commit
b375086cee
@ -108,6 +108,15 @@ if(NOT FTPD_CLASSIC AND NOT NINTENDO_DS)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT UNIX)
|
||||
target_sources(${FTPD_TARGET} PRIVATE
|
||||
source/posix/collate.c
|
||||
source/posix/collate.h
|
||||
source/posix/collcmp.c
|
||||
source/posix/glob.c
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NINTENDO_SWITCH)
|
||||
target_sources(${FTPD_TARGET} PRIVATE
|
||||
source/switch/init.c
|
||||
|
@ -26,6 +26,13 @@
|
||||
#include "platform.h"
|
||||
#include "socket.h"
|
||||
|
||||
#if __has_include(<glob.h>)
|
||||
#include <glob.h>
|
||||
#define FTPD_HAS_GLOB 1
|
||||
#else
|
||||
#define FTPD_HAS_GLOB 0
|
||||
#endif
|
||||
|
||||
#include <sys/stat.h>
|
||||
using stat_t = struct stat;
|
||||
|
||||
@ -217,6 +224,11 @@ private:
|
||||
/// \brief Transfer directory list
|
||||
bool listTransfer ();
|
||||
|
||||
#if FTPD_HAS_GLOB
|
||||
/// \brief Transfer glob list
|
||||
bool globTransfer ();
|
||||
#endif
|
||||
|
||||
/// \brief Transfer download
|
||||
bool retrieveTransfer ();
|
||||
|
||||
@ -303,6 +315,37 @@ private:
|
||||
/// \brief Directory being transferred
|
||||
fs::Dir m_dir;
|
||||
|
||||
#if FTPD_HAS_GLOB
|
||||
/// \brief Glob wrappre
|
||||
class Glob
|
||||
{
|
||||
public:
|
||||
~Glob () noexcept;
|
||||
Glob () noexcept;
|
||||
|
||||
/// \brief Perform glob
|
||||
/// \param pattern_ Glob pattern
|
||||
bool glob (char const *pattern_) noexcept;
|
||||
|
||||
/// \brief Get next glob result
|
||||
/// \note returns nullptr when no more entries exist
|
||||
char const *next () noexcept;
|
||||
|
||||
private:
|
||||
/// \brief Clear glob
|
||||
void clear () noexcept;
|
||||
|
||||
/// \brief Glob result
|
||||
std::optional<glob_t> m_glob = std::nullopt;
|
||||
|
||||
/// \brief Result counter
|
||||
unsigned m_offset = 0;
|
||||
};
|
||||
|
||||
/// \brief Glob
|
||||
Glob m_glob;
|
||||
#endif
|
||||
|
||||
/// \brief Directory transfer mode
|
||||
XferDirMode m_xferDirMode;
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2023 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -40,7 +40,7 @@ extern char const *const g_libnxCopyright;
|
||||
extern char const *const g_deko3dCopyright;
|
||||
extern char const *const g_zstdCopyright;
|
||||
extern char const *const g_libnxLicense;
|
||||
extern char const *const g_bsdLicense;
|
||||
extern char const *const g_zstdLicense;
|
||||
#endif
|
||||
|
||||
#if !defined(__NDS__)
|
||||
@ -50,4 +50,12 @@ extern char const *const g_zlibLicense;
|
||||
#if !defined(__NDS__) && !defined(__3DS__) && !defined(__SWITCH__)
|
||||
extern char const *const g_glfwVersion;
|
||||
extern char const *const g_glfwCopyright;
|
||||
#else
|
||||
extern char const *const g_globVersion;
|
||||
extern char const *const g_globCopyright;
|
||||
extern char const *const g_globLicense;
|
||||
|
||||
extern char const *const g_collateVersion;
|
||||
extern char const *const g_collateCopyright;
|
||||
extern char const *const g_collateLicense;
|
||||
#endif
|
||||
|
@ -877,7 +877,7 @@ void FtpServer::showAbout ()
|
||||
{
|
||||
ImGui::TextWrapped ("%s", g_zstdCopyright);
|
||||
ImGui::Separator ();
|
||||
ImGui::TextWrapped ("%s", g_bsdLicense);
|
||||
ImGui::TextWrapped ("%s", g_zstdLicense);
|
||||
ImGui::TreePop ();
|
||||
}
|
||||
#else
|
||||
@ -890,6 +890,24 @@ void FtpServer::showAbout ()
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__)
|
||||
if (ImGui::TreeNode (g_globVersion))
|
||||
{
|
||||
ImGui::TextWrapped ("%s", g_globCopyright);
|
||||
ImGui::Separator ();
|
||||
ImGui::TextWrapped ("%s", g_globLicense);
|
||||
ImGui::TreePop ();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode (g_collateVersion))
|
||||
{
|
||||
ImGui::TextWrapped ("%s", g_collateCopyright);
|
||||
ImGui::Separator ();
|
||||
ImGui::TextWrapped ("%s", g_collateLicense);
|
||||
ImGui::TreePop ();
|
||||
}
|
||||
#endif
|
||||
|
||||
ImGui::EndPopup ();
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,10 @@
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if FTPD_HAS_GLOB
|
||||
#include <glob.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
@ -297,6 +301,66 @@ std::string buildResolvedPath (std::string_view const cwd_, std::string_view con
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
#if FTPD_HAS_GLOB
|
||||
FtpSession::Glob::~Glob () noexcept
|
||||
{
|
||||
clear ();
|
||||
}
|
||||
|
||||
FtpSession::Glob::Glob () noexcept = default;
|
||||
|
||||
bool FtpSession::Glob::glob (char const *const pattern_) noexcept
|
||||
{
|
||||
if (!m_glob.has_value ())
|
||||
m_glob.emplace ();
|
||||
else
|
||||
::globfree (&m_glob.value ());
|
||||
|
||||
std::memset (&m_glob.value (), 0, sizeof (glob_t));
|
||||
|
||||
auto const rc = ::glob (pattern_, GLOB_NOSORT, nullptr, &m_glob.value ());
|
||||
if (rc == GLOB_NOSPACE)
|
||||
{
|
||||
clear ();
|
||||
errno = ENOMEM;
|
||||
return false;
|
||||
}
|
||||
else if (rc != 0)
|
||||
{
|
||||
clear ();
|
||||
errno = EIO;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_offset = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
char const *FtpSession::Glob::next () noexcept
|
||||
{
|
||||
if (!m_glob.has_value ())
|
||||
return nullptr;
|
||||
|
||||
if (m_glob->gl_pathc <= 0 || m_offset >= static_cast<unsigned> (m_glob->gl_pathc))
|
||||
{
|
||||
clear ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m_glob->gl_pathv[m_offset++];
|
||||
}
|
||||
|
||||
void FtpSession::Glob::clear () noexcept
|
||||
{
|
||||
if (!m_glob.has_value ())
|
||||
return;
|
||||
|
||||
::globfree (&m_glob.value ());
|
||||
m_glob.reset ();
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
FtpSession::~FtpSession ()
|
||||
{
|
||||
@ -1909,6 +1973,62 @@ bool FtpSession::listTransfer ()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FtpSession::globTransfer ()
|
||||
{
|
||||
#if FTPD_HAS_GLOB
|
||||
// check if we sent all available data
|
||||
if (m_xferBuffer.empty ())
|
||||
{
|
||||
m_xferBuffer.clear ();
|
||||
|
||||
auto const entry = m_glob.next ();
|
||||
if (!entry)
|
||||
{
|
||||
// we have exhausted the glob listing
|
||||
sendResponse ("226 OK\r\n");
|
||||
setState (State::COMMAND, true, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// NLST gives the whole path name
|
||||
auto const path = encodePath (entry) + "\r\n";
|
||||
if (m_xferBuffer.freeSize () < path.size ())
|
||||
{
|
||||
sendResponse ("501 %s\r\n", std::strerror (ENOMEM));
|
||||
setState (State::COMMAND, true, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::memcpy (m_xferBuffer.freeArea (), path.data (), path.size ());
|
||||
m_xferBuffer.markUsed (path.size ());
|
||||
LOCKED (m_filePosition += path.size ());
|
||||
}
|
||||
|
||||
// send any pending data
|
||||
auto const rc = m_dataSocket->write (m_xferBuffer);
|
||||
if (rc <= 0)
|
||||
{
|
||||
// error sending data
|
||||
if (rc < 0 && errno == EWOULDBLOCK)
|
||||
return false;
|
||||
|
||||
sendResponse ("426 Connection broken during transfer\r\n");
|
||||
setState (State::COMMAND, true, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_timestamp = std::time (nullptr);
|
||||
|
||||
// we can try to send more data
|
||||
return true;
|
||||
#else
|
||||
/// \todo error code?
|
||||
sendResponse ("451 Glob unsupported\r\n");
|
||||
setState (State::COMMAND, true, true);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FtpSession::retrieveTransfer ()
|
||||
{
|
||||
if (m_xferBuffer.empty ())
|
||||
@ -2265,7 +2385,40 @@ void FtpSession::NLST (char const *args_)
|
||||
return;
|
||||
}
|
||||
|
||||
// open the path in NLST mode
|
||||
#if FTPD_HAS_GLOB
|
||||
if (std::strchr (args_, '*'))
|
||||
{
|
||||
if (::chdir (m_cwd.c_str ()) != 0 || !m_glob.glob (args_))
|
||||
{
|
||||
sendResponse ("501 %s\r\n", std::strerror (errno));
|
||||
setState (State::COMMAND, false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_transfer = &FtpSession::globTransfer;
|
||||
|
||||
if (!m_port && !m_pasv)
|
||||
{
|
||||
// Prior PORT or PASV required
|
||||
sendResponse ("503 Bad sequence of commands\r\n");
|
||||
setState (State::COMMAND, true, true);
|
||||
return;
|
||||
}
|
||||
|
||||
setState (State::DATA_CONNECT, false, true);
|
||||
m_send = true;
|
||||
|
||||
// setup connection
|
||||
if (m_port && !dataConnect ())
|
||||
{
|
||||
sendResponse ("425 Can't open data connection\r\n");
|
||||
setState (State::COMMAND, true, true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
xferDir (args_, XferDirMode::NLST, false);
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2023 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -81,7 +81,7 @@ char const *const g_libnxLicense =
|
||||
"CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE "
|
||||
"OR PERFORMANCE OF THIS SOFTWARE.";
|
||||
|
||||
char const *const g_bsdLicense =
|
||||
char const *const g_zstdLicense =
|
||||
"BSD License\n"
|
||||
"\n"
|
||||
"Redistribution and use in source and binary forms, with or without modification, are "
|
||||
@ -134,4 +134,60 @@ char const *const g_zlibLicense =
|
||||
char const *const g_glfwVersion = "glfw " GLFW_VERSION_STRING;
|
||||
char const *const g_glfwCopyright = "Copyright (C) 2002-2006 Marcus Geelnard\n"
|
||||
"Copyright (C) 2006-2019 Camilla Löwy";
|
||||
#else
|
||||
char const *const g_globVersion = "glob.c 8.3 (Berkeley) 10/13/93";
|
||||
char const *const g_globCopyright =
|
||||
"Copyright (C) 1989-1993 The Regents of the University of California";
|
||||
char const *const g_globLicense =
|
||||
"This code is derived from software contributed to Berkeley by "
|
||||
"Guido van Rossum.\n"
|
||||
"\n"
|
||||
"Redistribution and use in source and binary forms, with or without "
|
||||
"modification, are permitted provided that the following conditions "
|
||||
"are met:\n"
|
||||
"1. Redistributions of source code must retain the above copyright "
|
||||
" notice, this list of conditions and the following disclaimer.\n"
|
||||
"2. Redistributions in binary form must reproduce the above copyright "
|
||||
" notice, this list of conditions and the following disclaimer in the "
|
||||
" documentation and/or other materials provided with the distribution.\n"
|
||||
"4. Neither the name of the University nor the names of its contributors "
|
||||
" may be used to endorse or promote products derived from this software "
|
||||
" without specific prior written permission.\n"
|
||||
"\n"
|
||||
"THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS \"AS IS\" AND "
|
||||
"ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE "
|
||||
"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE "
|
||||
"ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE "
|
||||
"FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL "
|
||||
"DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS "
|
||||
"OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) "
|
||||
"HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT "
|
||||
"LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY "
|
||||
"OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF "
|
||||
"SUCH DAMAGE.";
|
||||
|
||||
char const *const g_collateVersion = "FreeBSD: collate, v1.11 2002/03/21";
|
||||
char const *const g_collateCopyright = "Copyright (C) 1995 Alex Tatmanjants <alex@elvisti.kiev.ua> "
|
||||
"at Electronni Visti IA, Kiev, Ukraine.";
|
||||
char const *const g_collateLicense =
|
||||
"Redistribution and use in source and binary forms, with or without "
|
||||
"modification, are permitted provided that the following conditions "
|
||||
"are met:\n"
|
||||
"1. Redistributions of source code must retain the above copyright "
|
||||
" notice, this list of conditions and the following disclaimer.\n"
|
||||
"2. Redistributions in binary form must reproduce the above copyright "
|
||||
" notice, this list of conditions and the following disclaimer in the "
|
||||
" documentation and/or other materials provided with the distribution.\n"
|
||||
"\n"
|
||||
"THIS SOFTWARE IS PROVIDED BY THE AUTHOR \"AS IS\" AND "
|
||||
"ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE "
|
||||
"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE "
|
||||
"ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE "
|
||||
"FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL "
|
||||
"DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS "
|
||||
"OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) "
|
||||
"HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT "
|
||||
"LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY "
|
||||
"OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF "
|
||||
"SUCH DAMAGE.";
|
||||
#endif
|
||||
|
211
source/posix/collate.c
Normal file
211
source/posix/collate.c
Normal file
@ -0,0 +1,211 @@
|
||||
/*-
|
||||
* Copyright (c) 1995 Alex Tatmanjants <alex@elvisti.kiev.ua>
|
||||
* at Electronni Visti IA, Kiev, Ukraine.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "collate.h"
|
||||
|
||||
extern char *_PathLocale;
|
||||
int __collate_load_error = 1;
|
||||
int __collate_substitute_nontrivial;
|
||||
char __collate_version[STR_LEN];
|
||||
u_char __collate_substitute_table[UCHAR_MAX + 1][STR_LEN];
|
||||
struct __collate_st_char_pri __collate_char_pri_table[UCHAR_MAX + 1];
|
||||
struct __collate_st_chain_pri __collate_chain_pri_table[TABLE_SIZE];
|
||||
|
||||
#define FREAD(a, b, c, d) \
|
||||
do { \
|
||||
if (fread(a, b, c, d) != c) { \
|
||||
fclose(d); \
|
||||
return -1; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
void __collate_err(int ex, const char *f);
|
||||
|
||||
int
|
||||
__collate_load_tables(encoding)
|
||||
char *encoding;
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
FILE *fp;
|
||||
int i, save_load_error;
|
||||
|
||||
save_load_error = __collate_load_error;
|
||||
__collate_load_error = 1;
|
||||
if (!encoding) {
|
||||
__collate_load_error = save_load_error;
|
||||
return -1;
|
||||
}
|
||||
if (!strcmp(encoding, "C") || !strcmp(encoding, "POSIX"))
|
||||
return 0;
|
||||
if (!_PathLocale) {
|
||||
__collate_load_error = save_load_error;
|
||||
return -1;
|
||||
}
|
||||
/* Range checking not needed, encoding has fixed size */
|
||||
(void) strcpy(buf, _PathLocale);
|
||||
(void) strcat(buf, "/");
|
||||
(void) strcat(buf, encoding);
|
||||
(void) strcat(buf, "/LC_COLLATE");
|
||||
if ((fp = fopen(buf, "r")) == NULL) {
|
||||
__collate_load_error = save_load_error;
|
||||
return -1;
|
||||
}
|
||||
FREAD(__collate_version, sizeof(__collate_version), 1, fp);
|
||||
if (strcmp(__collate_version, COLLATE_VERSION) != 0) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
FREAD(__collate_substitute_table, sizeof(__collate_substitute_table),
|
||||
1, fp);
|
||||
FREAD(__collate_char_pri_table, sizeof(__collate_char_pri_table), 1,
|
||||
fp);
|
||||
FREAD(__collate_chain_pri_table, sizeof(__collate_chain_pri_table), 1,
|
||||
fp);
|
||||
fclose(fp);
|
||||
__collate_load_error = 0;
|
||||
|
||||
__collate_substitute_nontrivial = 0;
|
||||
for (i = 0; i < UCHAR_MAX + 1; i++) {
|
||||
if (__collate_substitute_table[i][0] != i ||
|
||||
__collate_substitute_table[i][1] != 0) {
|
||||
__collate_substitute_nontrivial = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u_char *
|
||||
__collate_substitute(s)
|
||||
const u_char *s;
|
||||
{
|
||||
int dest_len, len, nlen;
|
||||
int delta = strlen((const char *) s);
|
||||
u_char *dest_str = NULL;
|
||||
|
||||
if(s == NULL || *s == '\0')
|
||||
return __collate_strdup((u_char *) "");
|
||||
delta += delta / 8;
|
||||
dest_str = (u_char *) malloc(dest_len = delta);
|
||||
if(dest_str == NULL)
|
||||
__collate_err(EXIT_FAILURE, __FUNCTION__);
|
||||
len = 0;
|
||||
while(*s) {
|
||||
nlen = len + strlen((const char *)
|
||||
__collate_substitute_table[*s]);
|
||||
if (dest_len <= nlen) {
|
||||
dest_str = reallocf(dest_str, dest_len = nlen + delta);
|
||||
if(dest_str == NULL)
|
||||
__collate_err(EXIT_FAILURE, __FUNCTION__);
|
||||
}
|
||||
strcpy((char *) dest_str + len,
|
||||
(const char *) __collate_substitute_table[*s++]);
|
||||
len = nlen;
|
||||
}
|
||||
return dest_str;
|
||||
}
|
||||
|
||||
void
|
||||
__collate_lookup(t, len, prim, sec)
|
||||
const u_char *t;
|
||||
int *len, *prim, *sec;
|
||||
{
|
||||
struct __collate_st_chain_pri *p2;
|
||||
|
||||
*len = 1;
|
||||
*prim = *sec = 0;
|
||||
for(p2 = __collate_chain_pri_table; p2->str[0]; p2++) {
|
||||
if(strncmp((const char *) t, (const char *) p2->str,
|
||||
strlen((const char *) p2->str)) == 0) {
|
||||
*len = strlen((const char *) p2->str);
|
||||
*prim = p2->prim;
|
||||
*sec = p2->sec;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*prim = __collate_char_pri_table[*t].prim;
|
||||
*sec = __collate_char_pri_table[*t].sec;
|
||||
}
|
||||
|
||||
u_char *
|
||||
__collate_strdup(s)
|
||||
u_char *s;
|
||||
{
|
||||
u_char *t = (u_char *) strdup((const char *) s);
|
||||
|
||||
if (t == NULL)
|
||||
__collate_err(EXIT_FAILURE, __FUNCTION__);
|
||||
return t;
|
||||
}
|
||||
|
||||
void
|
||||
__collate_err(int ex, const char *f)
|
||||
{
|
||||
const char *s;
|
||||
int serrno = errno;
|
||||
int dummy;
|
||||
|
||||
/* Be careful to change write counts if you change the strings */
|
||||
write(STDERR_FILENO, "collate_error: ", 15);
|
||||
write(STDERR_FILENO, f, strlen(f));
|
||||
write(STDERR_FILENO, ": ", 2);
|
||||
s = _strerror_r(_REENT, serrno, 1, &dummy);
|
||||
write(STDERR_FILENO, s, strlen(s));
|
||||
write(STDERR_FILENO, "\n", 1);
|
||||
exit(ex);
|
||||
}
|
||||
|
||||
#ifdef COLLATE_DEBUG
|
||||
void
|
||||
__collate_print_tables()
|
||||
{
|
||||
int i;
|
||||
struct __collate_st_chain_pri *p2;
|
||||
|
||||
printf("Substitute table:\n");
|
||||
for (i = 0; i < UCHAR_MAX + 1; i++)
|
||||
if (i != *__collate_substitute_table[i])
|
||||
printf("\t'%c' --> \"%s\"\n", i,
|
||||
__collate_substitute_table[i]);
|
||||
printf("Chain priority table:\n");
|
||||
for (p2 = __collate_chain_pri_table; p2->str[0]; p2++)
|
||||
printf("\t\"%s\" : %d %d\n\n", p2->str, p2->prim, p2->sec);
|
||||
printf("Char priority table:\n");
|
||||
for (i = 0; i < UCHAR_MAX + 1; i++)
|
||||
printf("\t'%c' : %d %d\n", i, __collate_char_pri_table[i].prim,
|
||||
__collate_char_pri_table[i].sec);
|
||||
}
|
||||
#endif
|
67
source/posix/collate.h
Normal file
67
source/posix/collate.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*-
|
||||
* Copyright (c) 1995 Alex Tatmanjants <alex@elvisti.kiev.ua>
|
||||
* at Electronni Visti IA, Kiev, Ukraine.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD: src/lib/libc/locale/collate.h,v 1.11 2002/03/21 22:46:54 obrien Exp $
|
||||
*/
|
||||
|
||||
#ifndef _COLLATE_H_
|
||||
#define _COLLATE_H_
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/types.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define STR_LEN 10
|
||||
#define TABLE_SIZE 100
|
||||
#define COLLATE_VERSION "1.0\n"
|
||||
|
||||
struct __collate_st_char_pri {
|
||||
int prim, sec;
|
||||
};
|
||||
struct __collate_st_chain_pri {
|
||||
u_char str[STR_LEN];
|
||||
int prim, sec;
|
||||
};
|
||||
|
||||
extern int __collate_load_error;
|
||||
extern int __collate_substitute_nontrivial;
|
||||
extern char __collate_version[STR_LEN];
|
||||
extern u_char __collate_substitute_table[UCHAR_MAX + 1][STR_LEN];
|
||||
extern struct __collate_st_char_pri __collate_char_pri_table[UCHAR_MAX + 1];
|
||||
extern struct __collate_st_chain_pri __collate_chain_pri_table[TABLE_SIZE];
|
||||
|
||||
__BEGIN_DECLS
|
||||
u_char *__collate_strdup(u_char *);
|
||||
u_char *__collate_substitute(const u_char *);
|
||||
int __collate_load_tables(char *);
|
||||
void __collate_lookup(const u_char *, int *, int *, int *);
|
||||
int __collate_range_cmp(int, int);
|
||||
#ifdef COLLATE_DEBUG
|
||||
void __collate_print_tables(void);
|
||||
#endif
|
||||
__END_DECLS
|
||||
|
||||
#endif /* !_COLLATE_H_ */
|
84
source/posix/collcmp.c
Normal file
84
source/posix/collcmp.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 1996 by Andrey A. Chernov, Moscow, Russia.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#define ASCII_COMPATIBLE_COLLATE /* see share/colldef */
|
||||
|
||||
#include <string.h>
|
||||
#include "collate.h"
|
||||
#ifndef ASCII_COMPATIBLE_COLLATE
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Compare two characters converting collate information
|
||||
* into ASCII-compatible range, it allows to handle
|
||||
* "[a-z]"-type ranges with national characters.
|
||||
*/
|
||||
|
||||
int __collate_range_cmp (c1, c2)
|
||||
int c1, c2;
|
||||
{
|
||||
static char s1[2], s2[2];
|
||||
int ret;
|
||||
#ifndef ASCII_COMPATIBLE_COLLATE
|
||||
int as1, as2, al1, al2;
|
||||
#endif
|
||||
|
||||
c1 &= UCHAR_MAX;
|
||||
c2 &= UCHAR_MAX;
|
||||
if (c1 == c2)
|
||||
return (0);
|
||||
|
||||
#ifndef ASCII_COMPATIBLE_COLLATE
|
||||
as1 = isascii(c1);
|
||||
as2 = isascii(c2);
|
||||
al1 = isalpha(c1);
|
||||
al2 = isalpha(c2);
|
||||
|
||||
if (as1 || as2 || al1 || al2) {
|
||||
if ((as1 && as2) || (!al1 && !al2))
|
||||
return (c1 - c2);
|
||||
if (al1 && !al2) {
|
||||
if (isupper(c1))
|
||||
return ('A' - c2);
|
||||
else
|
||||
return ('a' - c2);
|
||||
} else if (al2 && !al1) {
|
||||
if (isupper(c2))
|
||||
return (c1 - 'A');
|
||||
else
|
||||
return (c1 - 'a');
|
||||
}
|
||||
}
|
||||
#endif
|
||||
s1[0] = c1;
|
||||
s2[0] = c2;
|
||||
if ((ret = strcoll(s1, s2)) != 0)
|
||||
return (ret);
|
||||
return (c1 - c2);
|
||||
}
|
836
source/posix/glob.c
Normal file
836
source/posix/glob.c
Normal file
@ -0,0 +1,836 @@
|
||||
/*
|
||||
* Copyright (c) 1989, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Guido van Rossum.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#define _NO_GLOB /* Cygwin provides its own glob. */
|
||||
#endif
|
||||
|
||||
#ifndef _NO_GLOB
|
||||
|
||||
#if defined(LIBC_SCCS) && !defined(lint)
|
||||
static char sccsid[] = "@(#)glob.c 8.3 (Berkeley) 10/13/93";
|
||||
#endif /* LIBC_SCCS and not lint */
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
/*
|
||||
* glob(3) -- a superset of the one defined in POSIX 1003.2.
|
||||
*
|
||||
* The [!...] convention to negate a range is supported (SysV, Posix, ksh).
|
||||
*
|
||||
* Optional extra services, controlled by flags not defined by POSIX:
|
||||
*
|
||||
* GLOB_QUOTE:
|
||||
* Escaping convention: \ inhibits any special meaning the following
|
||||
* character might have (except \ at end of string is retained).
|
||||
* GLOB_MAGCHAR:
|
||||
* Set in gl_flags if pattern contained a globbing character.
|
||||
* GLOB_NOMAGIC:
|
||||
* Same as GLOB_NOCHECK, but it will only append pattern if it did
|
||||
* not contain any magic characters. [Used in csh style globbing]
|
||||
* GLOB_ALTDIRFUNC:
|
||||
* Use alternately specified directory access functions.
|
||||
* GLOB_TILDE:
|
||||
* expand ~user/foo to the /home/dir/of/user/foo
|
||||
* GLOB_BRACE:
|
||||
* expand {1,2}{a,b} to 1a 1b 2a 2b
|
||||
* gl_matchc:
|
||||
* Number of matches in the current invocation of glob.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <glob.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "collate.h"
|
||||
|
||||
#define DOLLAR '$'
|
||||
#define DOT '.'
|
||||
#define EOS '\0'
|
||||
#define LBRACKET '['
|
||||
#define NOT '!'
|
||||
#define QUESTION '?'
|
||||
#define QUOTE '\\'
|
||||
#define RANGE '-'
|
||||
#define RBRACKET ']'
|
||||
#define SEP '/'
|
||||
#define STAR '*'
|
||||
#define TILDE '~'
|
||||
#define UNDERSCORE '_'
|
||||
#define LBRACE '{'
|
||||
#define RBRACE '}'
|
||||
#define SLASH '/'
|
||||
#define COMMA ','
|
||||
|
||||
#ifndef DEBUG
|
||||
|
||||
#define M_QUOTE 0x8000
|
||||
#define M_PROTECT 0x4000
|
||||
#define M_MASK 0xffff
|
||||
#define M_ASCII 0x00ff
|
||||
|
||||
typedef u_short Char;
|
||||
|
||||
#else
|
||||
|
||||
#define M_QUOTE 0x80
|
||||
#define M_PROTECT 0x40
|
||||
#define M_MASK 0xff
|
||||
#define M_ASCII 0x7f
|
||||
|
||||
typedef char Char;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#define CHAR(c) ((Char)((c)&M_ASCII))
|
||||
#define META(c) ((Char)((c)|M_QUOTE))
|
||||
#define M_ALL META('*')
|
||||
#define M_END META(']')
|
||||
#define M_NOT META('!')
|
||||
#define M_ONE META('?')
|
||||
#define M_RNG META('-')
|
||||
#define M_SET META('[')
|
||||
#define ismeta(c) (((c)&M_QUOTE) != 0)
|
||||
|
||||
|
||||
static int compare(const void *, const void *);
|
||||
static int g_Ctoc(const Char *, char *, u_int);
|
||||
static int g_lstat(Char *, struct stat *, glob_t *);
|
||||
static DIR *g_opendir(Char *, glob_t *);
|
||||
static Char *g_strchr(Char *, int);
|
||||
#ifdef notdef
|
||||
static Char *g_strcat(Char *, const Char *);
|
||||
#endif
|
||||
static int g_stat(Char *, struct stat *, glob_t *);
|
||||
static int glob0(const Char *, glob_t *, int *);
|
||||
static int glob1(Char *, glob_t *, int *);
|
||||
static int glob2(Char *, Char *, Char *, Char *, glob_t *, int *);
|
||||
static int glob3(Char *, Char *, Char *, Char *, Char *, glob_t *, int *);
|
||||
static int globextend(const Char *, glob_t *, int *);
|
||||
static int globexp1(const Char *, glob_t *, int *);
|
||||
static int globexp2(const Char *, const Char *, glob_t *, int *, int *);
|
||||
static int match(Char *, Char *, Char *);
|
||||
#ifdef DEBUG
|
||||
static void qprintf(const char *, Char *);
|
||||
#endif
|
||||
|
||||
int
|
||||
glob(pattern, flags, errfunc, pglob)
|
||||
const char *__restrict pattern;
|
||||
int flags, (*errfunc)(const char *, int);
|
||||
glob_t *__restrict pglob;
|
||||
{
|
||||
const u_char *patnext;
|
||||
int c, limit;
|
||||
Char *bufnext, *bufend, patbuf[MAXPATHLEN];
|
||||
|
||||
patnext = (u_char *) pattern;
|
||||
if (!(flags & GLOB_APPEND)) {
|
||||
pglob->gl_pathc = 0;
|
||||
pglob->gl_pathv = NULL;
|
||||
if (!(flags & GLOB_DOOFFS))
|
||||
pglob->gl_offs = 0;
|
||||
}
|
||||
if (flags & GLOB_LIMIT) {
|
||||
limit = pglob->gl_matchc;
|
||||
if (limit == 0)
|
||||
limit = ARG_MAX;
|
||||
} else
|
||||
limit = 0;
|
||||
pglob->gl_flags = flags & ~GLOB_MAGCHAR;
|
||||
pglob->gl_errfunc = errfunc;
|
||||
pglob->gl_matchc = 0;
|
||||
|
||||
bufnext = patbuf;
|
||||
bufend = bufnext + MAXPATHLEN - 1;
|
||||
if (flags & GLOB_QUOTE) {
|
||||
/* Protect the quoted characters. */
|
||||
while (bufnext < bufend && (c = *patnext++) != EOS)
|
||||
if (c == QUOTE) {
|
||||
if ((c = *patnext++) == EOS) {
|
||||
c = QUOTE;
|
||||
--patnext;
|
||||
}
|
||||
*bufnext++ = c | M_PROTECT;
|
||||
}
|
||||
else
|
||||
*bufnext++ = c;
|
||||
}
|
||||
else
|
||||
while (bufnext < bufend && (c = *patnext++) != EOS)
|
||||
*bufnext++ = c;
|
||||
*bufnext = EOS;
|
||||
|
||||
if (flags & GLOB_BRACE)
|
||||
return globexp1(patbuf, pglob, &limit);
|
||||
else
|
||||
return glob0(patbuf, pglob, &limit);
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand recursively a glob {} pattern. When there is no more expansion
|
||||
* invoke the standard globbing routine to glob the rest of the magic
|
||||
* characters
|
||||
*/
|
||||
static int
|
||||
globexp1(pattern, pglob, limit)
|
||||
const Char *pattern;
|
||||
glob_t *pglob;
|
||||
int *limit;
|
||||
{
|
||||
const Char* ptr = pattern;
|
||||
int rv;
|
||||
|
||||
/* Protect a single {}, for find(1), like csh */
|
||||
if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS)
|
||||
return glob0(pattern, pglob, limit);
|
||||
|
||||
while ((ptr = (const Char *) g_strchr((Char *) ptr, LBRACE)) != NULL)
|
||||
if (!globexp2(ptr, pattern, pglob, &rv, limit))
|
||||
return rv;
|
||||
|
||||
return glob0(pattern, pglob, limit);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Recursive brace globbing helper. Tries to expand a single brace.
|
||||
* If it succeeds then it invokes globexp1 with the new pattern.
|
||||
* If it fails then it tries to glob the rest of the pattern and returns.
|
||||
*/
|
||||
static int
|
||||
globexp2(ptr, pattern, pglob, rv, limit)
|
||||
const Char *ptr, *pattern;
|
||||
glob_t *pglob;
|
||||
int *rv, *limit;
|
||||
{
|
||||
int i;
|
||||
Char *lm, *ls;
|
||||
const Char *pe, *pm, *pl;
|
||||
Char patbuf[MAXPATHLEN];
|
||||
|
||||
/* copy part up to the brace */
|
||||
for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
|
||||
continue;
|
||||
*lm = EOS;
|
||||
ls = lm;
|
||||
|
||||
/* Find the balanced brace */
|
||||
for (i = 0, pe = ++ptr; *pe; pe++)
|
||||
if (*pe == LBRACKET) {
|
||||
/* Ignore everything between [] */
|
||||
for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++)
|
||||
continue;
|
||||
if (*pe == EOS) {
|
||||
/*
|
||||
* We could not find a matching RBRACKET.
|
||||
* Ignore and just look for RBRACE
|
||||
*/
|
||||
pe = pm;
|
||||
}
|
||||
}
|
||||
else if (*pe == LBRACE)
|
||||
i++;
|
||||
else if (*pe == RBRACE) {
|
||||
if (i == 0)
|
||||
break;
|
||||
i--;
|
||||
}
|
||||
|
||||
/* Non matching braces; just glob the pattern */
|
||||
if (i != 0 || *pe == EOS) {
|
||||
*rv = glob0(patbuf, pglob, limit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0, pl = pm = ptr; pm <= pe; pm++)
|
||||
switch (*pm) {
|
||||
case LBRACKET:
|
||||
/* Ignore everything between [] */
|
||||
for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++)
|
||||
continue;
|
||||
if (*pm == EOS) {
|
||||
/*
|
||||
* We could not find a matching RBRACKET.
|
||||
* Ignore and just look for RBRACE
|
||||
*/
|
||||
pm = pl;
|
||||
}
|
||||
break;
|
||||
|
||||
case LBRACE:
|
||||
i++;
|
||||
break;
|
||||
|
||||
case RBRACE:
|
||||
if (i) {
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case COMMA:
|
||||
if (i && *pm == COMMA)
|
||||
break;
|
||||
else {
|
||||
/* Append the current string */
|
||||
for (lm = ls; (pl < pm); *lm++ = *pl++)
|
||||
continue;
|
||||
/*
|
||||
* Append the rest of the pattern after the
|
||||
* closing brace
|
||||
*/
|
||||
for (pl = pe + 1; (*lm++ = *pl++) != EOS;)
|
||||
continue;
|
||||
|
||||
/* Expand the current pattern */
|
||||
#ifdef DEBUG
|
||||
qprintf("globexp2:", patbuf);
|
||||
#endif
|
||||
*rv = globexp1(patbuf, pglob, limit);
|
||||
|
||||
/* move after the comma, to the next string */
|
||||
pl = pm + 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
*rv = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* The main glob() routine: compiles the pattern (optionally processing
|
||||
* quotes), calls glob1() to do the real pattern matching, and finally
|
||||
* sorts the list (unless unsorted operation is requested). Returns 0
|
||||
* if things went well, nonzero if errors occurred. It is not an error
|
||||
* to find no matches.
|
||||
*/
|
||||
static int
|
||||
glob0(pattern, pglob, limit)
|
||||
const Char *pattern;
|
||||
glob_t *pglob;
|
||||
int *limit;
|
||||
{
|
||||
const Char *qpatnext;
|
||||
int c, err, oldpathc;
|
||||
Char *bufnext, patbuf[MAXPATHLEN];
|
||||
|
||||
qpatnext = pattern;
|
||||
oldpathc = pglob->gl_pathc;
|
||||
bufnext = patbuf;
|
||||
|
||||
/* We don't need to check for buffer overflow any more. */
|
||||
while ((c = *qpatnext++) != EOS) {
|
||||
switch (c) {
|
||||
case LBRACKET:
|
||||
c = *qpatnext;
|
||||
if (c == NOT)
|
||||
++qpatnext;
|
||||
if (*qpatnext == EOS ||
|
||||
g_strchr((Char *) qpatnext+1, RBRACKET) == NULL) {
|
||||
*bufnext++ = LBRACKET;
|
||||
if (c == NOT)
|
||||
--qpatnext;
|
||||
break;
|
||||
}
|
||||
*bufnext++ = M_SET;
|
||||
if (c == NOT)
|
||||
*bufnext++ = M_NOT;
|
||||
c = *qpatnext++;
|
||||
do {
|
||||
*bufnext++ = CHAR(c);
|
||||
if (*qpatnext == RANGE &&
|
||||
(c = qpatnext[1]) != RBRACKET) {
|
||||
*bufnext++ = M_RNG;
|
||||
*bufnext++ = CHAR(c);
|
||||
qpatnext += 2;
|
||||
}
|
||||
} while ((c = *qpatnext++) != RBRACKET);
|
||||
pglob->gl_flags |= GLOB_MAGCHAR;
|
||||
*bufnext++ = M_END;
|
||||
break;
|
||||
case QUESTION:
|
||||
pglob->gl_flags |= GLOB_MAGCHAR;
|
||||
*bufnext++ = M_ONE;
|
||||
break;
|
||||
case STAR:
|
||||
pglob->gl_flags |= GLOB_MAGCHAR;
|
||||
/* collapse adjacent stars to one,
|
||||
* to avoid exponential behavior
|
||||
*/
|
||||
if (bufnext == patbuf || bufnext[-1] != M_ALL)
|
||||
*bufnext++ = M_ALL;
|
||||
break;
|
||||
default:
|
||||
*bufnext++ = CHAR(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
*bufnext = EOS;
|
||||
#ifdef DEBUG
|
||||
qprintf("glob0:", patbuf);
|
||||
#endif
|
||||
|
||||
if ((err = glob1(patbuf, pglob, limit)) != 0)
|
||||
return(err);
|
||||
|
||||
/*
|
||||
* If there was no match we are going to append the pattern
|
||||
* if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
|
||||
* and the pattern did not contain any magic characters
|
||||
* GLOB_NOMAGIC is there just for compatibility with csh.
|
||||
*/
|
||||
if (pglob->gl_pathc == oldpathc &&
|
||||
((pglob->gl_flags & GLOB_NOCHECK) ||
|
||||
((pglob->gl_flags & GLOB_NOMAGIC) &&
|
||||
!(pglob->gl_flags & GLOB_MAGCHAR))))
|
||||
return(globextend(pattern, pglob, limit));
|
||||
else if (!(pglob->gl_flags & GLOB_NOSORT))
|
||||
qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
|
||||
pglob->gl_pathc - oldpathc, sizeof(char *), compare);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int
|
||||
compare(p, q)
|
||||
const void *p, *q;
|
||||
{
|
||||
return(strcmp(*(char **)p, *(char **)q));
|
||||
}
|
||||
|
||||
static int
|
||||
glob1(pattern, pglob, limit)
|
||||
Char *pattern;
|
||||
glob_t *pglob;
|
||||
int *limit;
|
||||
{
|
||||
Char pathbuf[MAXPATHLEN];
|
||||
|
||||
/* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
|
||||
if (*pattern == EOS)
|
||||
return(0);
|
||||
return(glob2(pathbuf, pathbuf, pathbuf + MAXPATHLEN - 1,
|
||||
pattern, pglob, limit));
|
||||
}
|
||||
|
||||
/*
|
||||
* The functions glob2 and glob3 are mutually recursive; there is one level
|
||||
* of recursion for each segment in the pattern that contains one or more
|
||||
* meta characters.
|
||||
*/
|
||||
static int
|
||||
glob2(pathbuf, pathend, pathend_last, pattern, pglob, limit)
|
||||
Char *pathbuf, *pathend, *pathend_last, *pattern;
|
||||
glob_t *pglob;
|
||||
int *limit;
|
||||
{
|
||||
struct stat sb;
|
||||
Char *p, *q;
|
||||
int anymeta;
|
||||
|
||||
/*
|
||||
* Loop over pattern segments until end of pattern or until
|
||||
* segment with meta character found.
|
||||
*/
|
||||
for (anymeta = 0;;) {
|
||||
if (*pattern == EOS) { /* End of pattern? */
|
||||
*pathend = EOS;
|
||||
if (g_lstat(pathbuf, &sb, pglob))
|
||||
return(0);
|
||||
|
||||
if (((pglob->gl_flags & GLOB_MARK) &&
|
||||
pathend[-1] != SEP) && (S_ISDIR(sb.st_mode)
|
||||
|| (S_ISLNK(sb.st_mode) &&
|
||||
(g_stat(pathbuf, &sb, pglob) == 0) &&
|
||||
S_ISDIR(sb.st_mode)))) {
|
||||
if (pathend + 1 > pathend_last)
|
||||
return (1);
|
||||
*pathend++ = SEP;
|
||||
*pathend = EOS;
|
||||
}
|
||||
++pglob->gl_matchc;
|
||||
return(globextend(pathbuf, pglob, limit));
|
||||
}
|
||||
|
||||
/* Find end of next segment, copy tentatively to pathend. */
|
||||
q = pathend;
|
||||
p = pattern;
|
||||
while (*p != EOS && *p != SEP) {
|
||||
if (ismeta(*p))
|
||||
anymeta = 1;
|
||||
if (q + 1 > pathend_last)
|
||||
return (1);
|
||||
*q++ = *p++;
|
||||
}
|
||||
|
||||
if (!anymeta) { /* No expansion, do next segment. */
|
||||
pathend = q;
|
||||
pattern = p;
|
||||
while (*pattern == SEP) {
|
||||
if (pathend + 1 > pathend_last)
|
||||
return (1);
|
||||
*pathend++ = *pattern++;
|
||||
}
|
||||
} else /* Need expansion, recurse. */
|
||||
return(glob3(pathbuf, pathend, pathend_last, pattern, p,
|
||||
pglob, limit));
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
static int
|
||||
glob3(pathbuf, pathend, pathend_last, pattern, restpattern, pglob, limit)
|
||||
Char *pathbuf, *pathend, *pathend_last, *pattern, *restpattern;
|
||||
glob_t *pglob;
|
||||
int *limit;
|
||||
{
|
||||
struct dirent *dp;
|
||||
DIR *dirp;
|
||||
int err;
|
||||
char buf[MAXPATHLEN];
|
||||
|
||||
/*
|
||||
* The readdirfunc declaration can't be prototyped, because it is
|
||||
* assigned, below, to two functions which are prototyped in glob.h
|
||||
* and dirent.h as taking pointers to differently typed opaque
|
||||
* structures.
|
||||
*/
|
||||
struct dirent *(*readdirfunc)();
|
||||
|
||||
if (pathend > pathend_last)
|
||||
return (1);
|
||||
*pathend = EOS;
|
||||
errno = 0;
|
||||
|
||||
if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
|
||||
/* TODO: don't call for ENOENT or ENOTDIR? */
|
||||
if (pglob->gl_errfunc) {
|
||||
if (g_Ctoc(pathbuf, buf, sizeof(buf)))
|
||||
return (GLOB_ABEND);
|
||||
if (pglob->gl_errfunc(buf, errno) ||
|
||||
pglob->gl_flags & GLOB_ERR)
|
||||
return (GLOB_ABEND);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
/* Search directory for matching names. */
|
||||
if (pglob->gl_flags & GLOB_ALTDIRFUNC)
|
||||
readdirfunc = pglob->gl_readdir;
|
||||
else
|
||||
readdirfunc = readdir;
|
||||
while ((dp = (*readdirfunc)(dirp))) {
|
||||
u_char *sc;
|
||||
Char *dc;
|
||||
|
||||
/* Initial DOT must be matched literally. */
|
||||
if (dp->d_name[0] == DOT && *pattern != DOT)
|
||||
continue;
|
||||
dc = pathend;
|
||||
sc = (u_char *) dp->d_name;
|
||||
while (dc < pathend_last && (*dc++ = *sc++) != EOS)
|
||||
;
|
||||
if (!match(pathend, pattern, restpattern)) {
|
||||
*pathend = EOS;
|
||||
continue;
|
||||
}
|
||||
err = glob2(pathbuf, --dc, pathend_last, restpattern,
|
||||
pglob, limit);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
if (pglob->gl_flags & GLOB_ALTDIRFUNC)
|
||||
(*pglob->gl_closedir)(dirp);
|
||||
else
|
||||
closedir(dirp);
|
||||
return(err);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Extend the gl_pathv member of a glob_t structure to accomodate a new item,
|
||||
* add the new item, and update gl_pathc.
|
||||
*
|
||||
* This assumes the BSD realloc, which only copies the block when its size
|
||||
* crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
|
||||
* behavior.
|
||||
*
|
||||
* Return 0 if new item added, error code if memory couldn't be allocated.
|
||||
*
|
||||
* Invariant of the glob_t structure:
|
||||
* Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
|
||||
* gl_pathv points to (gl_offs + gl_pathc + 1) items.
|
||||
*/
|
||||
static int
|
||||
globextend(path, pglob, limit)
|
||||
const Char *path;
|
||||
glob_t *pglob;
|
||||
int *limit;
|
||||
{
|
||||
char **pathv;
|
||||
int i;
|
||||
u_int newsize, len;
|
||||
char *copy;
|
||||
const Char *p;
|
||||
|
||||
if (*limit && pglob->gl_pathc > *limit) {
|
||||
errno = 0;
|
||||
return (GLOB_NOSPACE);
|
||||
}
|
||||
|
||||
newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
|
||||
pathv = pglob->gl_pathv ?
|
||||
realloc((char *)pglob->gl_pathv, newsize) :
|
||||
malloc(newsize);
|
||||
if (pathv == NULL) {
|
||||
if (pglob->gl_pathv) {
|
||||
free(pglob->gl_pathv);
|
||||
pglob->gl_pathv = NULL;
|
||||
}
|
||||
return(GLOB_NOSPACE);
|
||||
}
|
||||
|
||||
if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
|
||||
/* first time around -- clear initial gl_offs items */
|
||||
pathv += pglob->gl_offs;
|
||||
for (i = pglob->gl_offs; --i >= 0; )
|
||||
*--pathv = NULL;
|
||||
}
|
||||
pglob->gl_pathv = pathv;
|
||||
|
||||
for (p = path; *p++;)
|
||||
continue;
|
||||
len = (size_t)(p - path);
|
||||
if ((copy = malloc(len)) != NULL) {
|
||||
if (g_Ctoc(path, copy, len)) {
|
||||
free(copy);
|
||||
return (GLOB_NOSPACE);
|
||||
}
|
||||
pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
|
||||
}
|
||||
pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
|
||||
return(copy == NULL ? GLOB_NOSPACE : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* pattern matching function for filenames. Each occurrence of the *
|
||||
* pattern causes a recursion level.
|
||||
*/
|
||||
static int
|
||||
match(name, pat, patend)
|
||||
Char *name, *pat, *patend;
|
||||
{
|
||||
int ok, negate_range;
|
||||
Char c, k;
|
||||
|
||||
while (pat < patend) {
|
||||
c = *pat++;
|
||||
switch (c & M_MASK) {
|
||||
case M_ALL:
|
||||
if (pat == patend)
|
||||
return(1);
|
||||
do
|
||||
if (match(name, pat, patend))
|
||||
return(1);
|
||||
while (*name++ != EOS);
|
||||
return(0);
|
||||
case M_ONE:
|
||||
if (*name++ == EOS)
|
||||
return(0);
|
||||
break;
|
||||
case M_SET:
|
||||
ok = 0;
|
||||
if ((k = *name++) == EOS)
|
||||
return(0);
|
||||
if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS)
|
||||
++pat;
|
||||
while (((c = *pat++) & M_MASK) != M_END)
|
||||
if ((*pat & M_MASK) == M_RNG) {
|
||||
if (__collate_load_error ?
|
||||
CHAR(c) <= CHAR(k) && CHAR(k) <= CHAR(pat[1]) :
|
||||
__collate_range_cmp(CHAR(c), CHAR(k)) <= 0
|
||||
&& __collate_range_cmp(CHAR(k), CHAR(pat[1])) <= 0
|
||||
)
|
||||
ok = 1;
|
||||
pat += 2;
|
||||
} else if (c == k)
|
||||
ok = 1;
|
||||
if (ok == negate_range)
|
||||
return(0);
|
||||
break;
|
||||
default:
|
||||
if (*name++ != c)
|
||||
return(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return(*name == EOS);
|
||||
}
|
||||
|
||||
/* Free allocated data belonging to a glob_t structure. */
|
||||
void
|
||||
globfree(pglob)
|
||||
glob_t *pglob;
|
||||
{
|
||||
int i;
|
||||
char **pp;
|
||||
|
||||
if (pglob->gl_pathv != NULL) {
|
||||
pp = pglob->gl_pathv + pglob->gl_offs;
|
||||
for (i = pglob->gl_pathc; i--; ++pp)
|
||||
if (*pp)
|
||||
free(*pp);
|
||||
free(pglob->gl_pathv);
|
||||
pglob->gl_pathv = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static DIR *
|
||||
g_opendir(str, pglob)
|
||||
Char *str;
|
||||
glob_t *pglob;
|
||||
{
|
||||
char buf[MAXPATHLEN];
|
||||
|
||||
if (!*str)
|
||||
strcpy(buf, ".");
|
||||
else {
|
||||
if (g_Ctoc(str, buf, sizeof(buf)))
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (pglob->gl_flags & GLOB_ALTDIRFUNC)
|
||||
return((*pglob->gl_opendir)(buf));
|
||||
|
||||
return(opendir(buf));
|
||||
}
|
||||
|
||||
static int
|
||||
g_lstat(fn, sb, pglob)
|
||||
Char *fn;
|
||||
struct stat *sb;
|
||||
glob_t *pglob;
|
||||
{
|
||||
char buf[MAXPATHLEN];
|
||||
|
||||
if (g_Ctoc(fn, buf, sizeof(buf))) {
|
||||
errno = ENAMETOOLONG;
|
||||
return (-1);
|
||||
}
|
||||
if (pglob->gl_flags & GLOB_ALTDIRFUNC)
|
||||
return((*pglob->gl_lstat)(buf, sb));
|
||||
return(lstat(buf, sb));
|
||||
}
|
||||
|
||||
static int
|
||||
g_stat(fn, sb, pglob)
|
||||
Char *fn;
|
||||
struct stat *sb;
|
||||
glob_t *pglob;
|
||||
{
|
||||
char buf[MAXPATHLEN];
|
||||
|
||||
if (g_Ctoc(fn, buf, sizeof(buf))) {
|
||||
errno = ENAMETOOLONG;
|
||||
return (-1);
|
||||
}
|
||||
if (pglob->gl_flags & GLOB_ALTDIRFUNC)
|
||||
return((*pglob->gl_stat)(buf, sb));
|
||||
return(stat(buf, sb));
|
||||
}
|
||||
|
||||
static Char *
|
||||
g_strchr(str, ch)
|
||||
Char *str;
|
||||
int ch;
|
||||
{
|
||||
do {
|
||||
if (*str == ch)
|
||||
return (str);
|
||||
} while (*str++);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
g_Ctoc(str, buf, len)
|
||||
const Char *str;
|
||||
char *buf;
|
||||
u_int len;
|
||||
{
|
||||
|
||||
while (len--) {
|
||||
if ((*buf++ = *str++) == '\0')
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
qprintf(str, s)
|
||||
const char *str;
|
||||
Char *s;
|
||||
{
|
||||
Char *p;
|
||||
|
||||
(void)printf("%s:\n", str);
|
||||
for (p = s; *p; p++)
|
||||
(void)printf("%c", CHAR(*p));
|
||||
(void)printf("\n");
|
||||
for (p = s; *p; p++)
|
||||
(void)printf("%c", *p & M_PROTECT ? '"' : ' ');
|
||||
(void)printf("\n");
|
||||
for (p = s; *p; p++)
|
||||
(void)printf("%c", ismeta(*p) ? '_' : ' ');
|
||||
(void)printf("\n");
|
||||
}
|
||||
#endif
|
||||
#endif /* !_NO_GLOB */
|
Loading…
Reference in New Issue
Block a user