From: Colin Leroy-Mira Date: Sat, 16 Mar 2024 11:39:01 +0000 (+0100) Subject: file: add support for getting basic directory listings X-Git-Tag: curl-8_8_0~246 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bfe54b0e88239da542493321e795cd71c14af9cc;p=thirdparty%2Fcurl.git file: add support for getting basic directory listings Not supported on Windows (yet) Closes #13137 --- diff --git a/CMake/Platforms/WindowsCache.cmake b/CMake/Platforms/WindowsCache.cmake index d3391d92f8..082154ff9f 100644 --- a/CMake/Platforms/WindowsCache.cmake +++ b/CMake/Platforms/WindowsCache.cmake @@ -137,6 +137,9 @@ set(HAVE_TERMIOS_H 0) set(HAVE_TERMIO_H 0) set(HAVE_UTIME_H 0) # mingw-w64 has it (wrapper to sys/utime.h) +set(HAVE_DIRENT_H 0) +set(HAVE_OPENDIR 0) + set(HAVE_FSEEKO 0) set(HAVE__FSEEKI64 1) set(HAVE_SOCKET 1) diff --git a/CMakeLists.txt b/CMakeLists.txt index bfc062759c..433dc51a84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1126,6 +1126,7 @@ check_include_file_concat("sys/un.h" HAVE_SYS_UN_H) check_include_file_concat("sys/utime.h" HAVE_SYS_UTIME_H) check_include_file_concat("sys/xattr.h" HAVE_SYS_XATTR_H) check_include_file_concat("arpa/inet.h" HAVE_ARPA_INET_H) +check_include_file_concat("dirent.h" HAVE_DIRENT_H) check_include_file_concat("fcntl.h" HAVE_FCNTL_H) check_include_file_concat("ifaddrs.h" HAVE_IFADDRS_H) check_include_file_concat("io.h" HAVE_IO_H) @@ -1188,6 +1189,7 @@ endif() check_symbol_exists(fnmatch "${CURL_INCLUDES};fnmatch.h" HAVE_FNMATCH) check_symbol_exists(basename "${CURL_INCLUDES};string.h" HAVE_BASENAME) +check_symbol_exists(opendir "${CURL_INCLUDES};dirent.h" HAVE_OPENDIR) check_symbol_exists(socket "${CURL_INCLUDES}" HAVE_SOCKET) check_symbol_exists(sched_yield "${CURL_INCLUDES};sched.h" HAVE_SCHED_YIELD) check_symbol_exists(socketpair "${CURL_INCLUDES}" HAVE_SOCKETPAIR) diff --git a/configure.ac b/configure.ac index 0a356e9efd..9bc7fabd82 100644 --- a/configure.ac +++ b/configure.ac @@ -3984,6 +3984,12 @@ if test "$want_thres" = "yes" && test "x$USE_THREADS_POSIX" != "x1"; then fi fi +AC_CHECK_HEADER(dirent.h, + [ AC_DEFINE(HAVE_DIRENT_H, 1, [if you have ]) + AC_CHECK_FUNC(opendir, AC_DEFINE(HAVE_OPENDIR, 1, [if you have opendir]) ) + ] +) + CURL_CONVERT_INCLUDE_TO_ISYSTEM dnl ************************************************************ diff --git a/docs/cmdline-opts/list-only.md b/docs/cmdline-opts/list-only.md index 5d8dde65c2..2800a8f793 100644 --- a/docs/cmdline-opts/list-only.md +++ b/docs/cmdline-opts/list-only.md @@ -3,10 +3,10 @@ c: Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl Long: list-only Short: l -Protocols: FTP POP3 SFTP +Protocols: FTP POP3 SFTP FILE Help: List only mode Added: 4.0 -Category: ftp pop3 sftp +Category: ftp pop3 sftp file Multi: boolean See-also: - quote @@ -35,6 +35,9 @@ When retrieving a specific email from POP3, this switch forces a LIST command to be performed instead of RETR. This is particularly useful if the user wants to see if a specific message-id exists on the server and what size it is. +For FILE, this option has no effect yet as directories are always listed in +this mode. + Note: When combined with --request, this option can be used to send a UIDL command instead, so the user may use the email's unique identifier rather than its message-id to make the request. diff --git a/docs/libcurl/opts/CURLOPT_DIRLISTONLY.md b/docs/libcurl/opts/CURLOPT_DIRLISTONLY.md index 8775d16f10..348faf386d 100644 --- a/docs/libcurl/opts/CURLOPT_DIRLISTONLY.md +++ b/docs/libcurl/opts/CURLOPT_DIRLISTONLY.md @@ -36,6 +36,9 @@ messages on the POP3 server. This can be used to change the default behavior of libcurl, when combined with a URL that contains a message ID, to perform a "scan listing" which can then be used to determine the size of an email. +For FILE, this option has no effect yet as directories are always listed in +this mode. + Note: For FTP this causes a NLST command to be sent to the FTP server. Beware that some FTP servers list only files in their response to NLST; they might not include subdirectories and symbolic links. diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake index 0f4db69820..dbdd04b5ad 100644 --- a/lib/curl_config.h.cmake +++ b/lib/curl_config.h.cmake @@ -199,6 +199,12 @@ /* Define to 1 if you have the `closesocket' function. */ #cmakedefine HAVE_CLOSESOCKET 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_DIRENT_H 1 + +/* Define to 1 if you have the `opendir' function. */ +#cmakedefine HAVE_OPENDIR 1 + /* Define to 1 if you have the fcntl function. */ #cmakedefine HAVE_FCNTL 1 diff --git a/lib/file.c b/lib/file.c index c436aaaad4..70328fabd9 100644 --- a/lib/file.c +++ b/lib/file.c @@ -50,6 +50,14 @@ #include #endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_DIRENT_H +#include +#endif + #include "strtoofft.h" #include "urldata.h" #include @@ -544,49 +552,85 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) Curl_pgrsSetDownloadSize(data, expected_size); if(data->state.resume_from) { - if(data->state.resume_from != - lseek(fd, data->state.resume_from, SEEK_SET)) + if(!S_ISDIR(statbuf.st_mode)) { + if(data->state.resume_from != + lseek(fd, data->state.resume_from, SEEK_SET)) + return CURLE_BAD_DOWNLOAD_RESUME; + } + else { return CURLE_BAD_DOWNLOAD_RESUME; + } } result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen); if(result) goto out; - while(!result) { - ssize_t nread; - /* Don't fill a whole buffer if we want less than all data */ - size_t bytestoread; + if(!S_ISDIR(statbuf.st_mode)) { + while(!result) { + ssize_t nread; + /* Don't fill a whole buffer if we want less than all data */ + size_t bytestoread; - if(size_known) { - bytestoread = (expected_size < (curl_off_t)(xfer_blen-1)) ? - curlx_sotouz(expected_size) : (xfer_blen-1); - } - else - bytestoread = xfer_blen-1; + if(size_known) { + bytestoread = (expected_size < (curl_off_t)(xfer_blen-1)) ? + curlx_sotouz(expected_size) : (xfer_blen-1); + } + else + bytestoread = xfer_blen-1; - nread = read(fd, xfer_buf, bytestoread); + nread = read(fd, xfer_buf, bytestoread); - if(nread > 0) - xfer_buf[nread] = 0; + if(nread > 0) + xfer_buf[nread] = 0; - if(nread <= 0 || (size_known && (expected_size == 0))) - break; + if(nread <= 0 || (size_known && (expected_size == 0))) + break; - if(size_known) - expected_size -= nread; + if(size_known) + expected_size -= nread; - result = Curl_client_write(data, CLIENTWRITE_BODY, xfer_buf, nread); - if(result) - goto out; + result = Curl_client_write(data, CLIENTWRITE_BODY, xfer_buf, nread); + if(result) + goto out; - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else - result = Curl_speedcheck(data, Curl_now()); - if(result) + if(Curl_pgrsUpdate(data)) + result = CURLE_ABORTED_BY_CALLBACK; + else + result = Curl_speedcheck(data, Curl_now()); + if(result) + goto out; + } + } + else { +#ifdef HAVE_OPENDIR + DIR *dir = opendir(file->path); + struct dirent *entry; + + if(!dir) { + result = CURLE_READ_ERROR; goto out; + } + else { + while((entry = readdir(dir))) { + if(entry->d_name[0] != '.') { + result = Curl_client_write(data, CLIENTWRITE_BODY, + entry->d_name, strlen(entry->d_name)); + if(result) + break; + result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1); + if(result) + break; + } + } + closedir(dir); + } +#else + failf(data, "Directory listing not yet implemented on this platform."); + result = CURLE_READ_ERROR; +#endif } + if(Curl_pgrsUpdate(data)) result = CURLE_ABORTED_BY_CALLBACK; diff --git a/lib/urldata.h b/lib/urldata.h index 308c51c923..03f883496d 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -104,7 +104,7 @@ typedef unsigned int curl_prot_t; #define PROTO_FAMILY_SSH (CURLPROTO_SCP|CURLPROTO_SFTP) #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) || \ - !defined(CURL_DISABLE_POP3) + !defined(CURL_DISABLE_POP3) || !defined(CURL_DISABLE_FILE) /* these protocols support CURLOPT_DIRLISTONLY */ #define CURL_LIST_ONLY_PROTOCOL 1 #endif diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c index 5d93644054..adb01b3bed 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -338,7 +338,7 @@ const struct helptxt helptext[] = { CURLHELP_CONNECTION}, {"-l, --list-only", "List only mode", - CURLHELP_FTP | CURLHELP_POP3 | CURLHELP_SFTP}, + CURLHELP_FTP | CURLHELP_POP3 | CURLHELP_SFTP | CURLHELP_FILE}, {" --local-port ", "Use a local port number within RANGE", CURLHELP_CONNECTION}, diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index cfbfb7860c..a91b4b7ce5 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -261,4 +261,4 @@ test3024 test3025 test3026 test3027 test3028 test3029 test3030 \ \ test3100 test3101 test3102 test3103 \ test3200 \ -test3201 test3202 +test3201 test3202 test3203 diff --git a/tests/data/test1463 b/tests/data/test1463 index 254b51b222..68b3e4fb0d 100644 --- a/tests/data/test1463 +++ b/tests/data/test1463 @@ -39,6 +39,7 @@ Usage: curl [options...] file: FILE protocol options --create-file-mode File mode for created files -I, --head Show document info only + -l, --list-only List only mode -r, --range Retrieve only the bytes within RANGE diff --git a/tests/data/test1464 b/tests/data/test1464 index 6a0af34c29..186c6ee024 100644 --- a/tests/data/test1464 +++ b/tests/data/test1464 @@ -39,6 +39,7 @@ Usage: curl [options...] file: FILE protocol options --create-file-mode File mode for created files -I, --head Show document info only + -l, --list-only List only mode -r, --range Retrieve only the bytes within RANGE diff --git a/tests/data/test3203 b/tests/data/test3203 new file mode 100644 index 0000000000..527e870a49 --- /dev/null +++ b/tests/data/test3203 @@ -0,0 +1,41 @@ + + + +HTTP +HTTP GET +FILE + + + +# +# Client-side + + +file + + +GET a directory using file:// + + + +!win32 + + +file://localhost%FILE_PWD/%LOGDIR/test%TESTNUMBER.dir/ + + +Contents of file are irrelevant + + + +# +# Verify data after the test has been "shot" + + +0 + + +dir-listing-test.txt + + +