sftp needs a long time for sending a filelist
Damien Miller
djm at mindrot.org
Mon Jul 21 23:16:51 EST 2008
On Mon, 21 Jul 2008, Markus Wünsch wrote:
>
> Hello all
> Im using sftp 1:4.7p1-8ubuntu1.2
> in a batchjob
> Ive noticed that sftp needs a long time for sending a filelist.
> The timespan increases exponential if many files are on the
> remoteserver.
> for example "ls -la *.txt" needs 10 seconds for 2000 files
> but needs 50 seconds for 4000 files.
> For 150.000 Files i have to wait 15 minutes for example
> but the Conecction is very fast. I think the sftp-server needs this
> time to
> build the list.
There are two performance bugs in the sftp client related to directory
listings:
1. The sftp-client.c do_readdir() makes a server round trip per batch of
filenames rather than doing pipelined reads like do_upload/do_download
2. The sftp.c code then does a server round trip per filename to refetch
the attrib (stat) data for each file. This is especially wasteful,
because this data was already sent via do_readdir()
> What can i do to get the list in normal time. ?
Fixing this is on my (long) todo list. Here is a patch that I made some
time back to fix #2, but never fully tested (updated to -current). Fixing
#1 would probably be the work of an evening if someone is interested.
-d
Index: sftp-client.c
===================================================================
RCS file: /cvs/src/usr.bin/ssh/sftp-client.c,v
retrieving revision 1.86
diff -u -p -r1.86 sftp-client.c
--- sftp-client.c 26 Jun 2008 06:10:09 -0000 1.86
+++ sftp-client.c 21 Jul 2008 13:11:46 -0000
@@ -465,6 +465,7 @@ do_lsreaddir(struct sftp_conn *conn, cha
if (count == 0)
break;
debug3("Received %d SSH2_FXP_NAME responses", count);
+ /* XXX - implement pipelined read here like do_download */
for (i = 0; i < count; i++) {
char *filename, *longname;
Attrib *a;
Index: sftp-glob.c
===================================================================
RCS file: /cvs/src/usr.bin/ssh/sftp-glob.c,v
retrieving revision 1.22
diff -u -p -r1.22 sftp-glob.c
--- sftp-glob.c 3 Aug 2006 03:34:42 -0000 1.22
+++ sftp-glob.c 21 Jul 2008 13:11:46 -0000
@@ -19,35 +19,31 @@
#include <sys/stat.h>
#include <dirent.h>
-#include <glob.h>
#include <string.h>
+#include "xglob.h"
#include "xmalloc.h"
#include "sftp.h"
#include "buffer.h"
#include "sftp-common.h"
#include "sftp-client.h"
-int remote_glob(struct sftp_conn *, const char *, int,
- int (*)(const char *, int), glob_t *);
+int remote_xglob(struct sftp_conn *, const char *, int,
+ int (*)(const char *, int), xglob_t *);
struct SFTP_OPENDIR {
SFTP_DIRENT **dir;
int offset;
};
-static struct {
- struct sftp_conn *conn;
-} cur;
-
static void *
-fudge_opendir(const char *path)
+remote_opendir(void *ctx, const char *path)
{
struct SFTP_OPENDIR *r;
+ struct sftp_conn *conn = ctx;
r = xmalloc(sizeof(*r));
-
- if (do_readdir(cur.conn, (char *)path, &r->dir)) {
+ if (do_readdir(conn, (char *)path, &r->dir)) {
xfree(r);
return(NULL);
}
@@ -58,33 +54,39 @@ fudge_opendir(const char *path)
}
static struct dirent *
-fudge_readdir(struct SFTP_OPENDIR *od)
+remote_readdir(void *ctx, void *od_)
{
+ struct SFTP_OPENDIR *od = od_;
static struct dirent ret;
if (od->dir[od->offset] == NULL)
return(NULL);
memset(&ret, 0, sizeof(ret));
- strlcpy(ret.d_name, od->dir[od->offset++]->filename,
+ strlcpy(ret.d_name, od->dir[od->offset]->filename,
sizeof(ret.d_name));
+ od->offset++;
return(&ret);
}
static void
-fudge_closedir(struct SFTP_OPENDIR *od)
+remote_closedir(void *ctx, void *od_)
{
+ struct SFTP_OPENDIR *od = od_;
+
free_sftp_dirents(od->dir);
xfree(od);
}
static int
-fudge_lstat(const char *path, struct stat *st)
+remote_lstat(void *ctx, const char *path, struct stat *st)
{
Attrib *a;
+ struct sftp_conn *conn = ctx;
- if (!(a = do_lstat(cur.conn, (char *)path, 0)))
+ a = do_lstat(conn, (char *)path, 0);
+ if (a == NULL)
return(-1);
attrib_to_stat(a, st);
@@ -93,11 +95,17 @@ fudge_lstat(const char *path, struct sta
}
static int
-fudge_stat(const char *path, struct stat *st)
+remote_stat(void *ctx, const char *path, struct stat *st)
{
Attrib *a;
+ struct sftp_conn *conn = ctx;
- if (!(a = do_stat(cur.conn, (char *)path, 0)))
+ /*
+ * XXX it is possible to skip another roundtrip here in
+ * some cases, where the Attrib information is in the results of
+ * a recent opendir()
+ */
+ if (!(a = do_stat(conn, (char *)path, 0)))
return(-1);
attrib_to_stat(a, st);
@@ -106,17 +114,15 @@ fudge_stat(const char *path, struct stat
}
int
-remote_glob(struct sftp_conn *conn, const char *pattern, int flags,
- int (*errfunc)(const char *, int), glob_t *pglob)
+remote_xglob(struct sftp_conn *conn, const char *pattern, int flags,
+ int (*errfunc)(const char *, int), xglob_t *pglob)
{
- pglob->gl_opendir = fudge_opendir;
- pglob->gl_readdir = (struct dirent *(*)(void *))fudge_readdir;
- pglob->gl_closedir = (void (*)(void *))fudge_closedir;
- pglob->gl_lstat = fudge_lstat;
- pglob->gl_stat = fudge_stat;
-
- memset(&cur, 0, sizeof(cur));
- cur.conn = conn;
+ pglob->gl_opendir = remote_opendir;
+ pglob->gl_readdir = remote_readdir;
+ pglob->gl_closedir = remote_closedir;
+ pglob->gl_lstat = remote_lstat;
+ pglob->gl_stat = remote_stat;
+ pglob->gl_user_ctx = conn;
- return(glob(pattern, flags | GLOB_ALTDIRFUNC, errfunc, pglob));
+ return(xglob(pattern, flags | XGLOB_ALTDIRFUNC, errfunc, pglob));
}
Index: sftp.c
===================================================================
RCS file: /cvs/src/usr.bin/ssh/sftp.c,v
retrieving revision 1.103
diff -u -p -r1.103 sftp.c
--- sftp.c 13 Jul 2008 22:16:03 -0000 1.103
+++ sftp.c 21 Jul 2008 13:11:46 -0000
@@ -25,7 +25,6 @@
#include <ctype.h>
#include <errno.h>
-#include <glob.h>
#include <histedit.h>
#include <paths.h>
#include <signal.h>
@@ -41,6 +40,7 @@
#include "pathnames.h"
#include "misc.h"
+#include "xglob.h"
#include "sftp.h"
#include "buffer.h"
#include "sftp-common.h"
@@ -70,8 +70,8 @@ volatile sig_atomic_t interrupted = 0;
/* I wish qsort() took a separate ctx for the comparison function...*/
int sort_flag;
-int remote_glob(struct sftp_conn *, const char *, int,
- int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
+int remote_xglob(struct sftp_conn *, const char *, int,
+ int (*)(const char *, int), xglob_t *); /* proto for sftp-glob.c */
/* Separators for interactive commands */
#define WHITESPACE " \t\r\n"
@@ -473,7 +473,7 @@ process_get(struct sftp_conn *conn, char
char *abs_src = NULL;
char *abs_dst = NULL;
char *tmp;
- glob_t g;
+ xglob_t g;
int err = 0;
int i;
@@ -482,7 +482,7 @@ process_get(struct sftp_conn *conn, char
memset(&g, 0, sizeof(g));
debug3("Looking up %s", abs_src);
- if (remote_glob(conn, abs_src, 0, NULL, &g)) {
+ if (remote_xglob(conn, abs_src, 0, NULL, &g)) {
error("File \"%s\" not found.", abs_src);
err = -1;
goto out;
@@ -496,8 +496,8 @@ process_get(struct sftp_conn *conn, char
goto out;
}
- for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
- if (infer_path(g.gl_pathv[i], &tmp)) {
+ for (i = 0; g.gl_matchv[i].gl_path && !interrupted; i++) {
+ if (infer_path(g.gl_matchv[i].gl_path, &tmp)) {
err = -1;
goto out;
}
@@ -506,7 +506,7 @@ process_get(struct sftp_conn *conn, char
/* If directory specified, append filename */
xfree(tmp);
if (is_dir(dst)) {
- if (infer_path(g.gl_pathv[0], &tmp)) {
+ if (infer_path(g.gl_matchv[0].gl_path, &tmp)) {
err = 1;
goto out;
}
@@ -520,8 +520,9 @@ process_get(struct sftp_conn *conn, char
} else
abs_dst = tmp;
- printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
- if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
+ printf("Fetching %s to %s\n", g.gl_matchv[i].gl_path, abs_dst);
+ if (do_download(conn, g.gl_matchv[i].gl_path, abs_dst,
+ pflag) == -1)
err = -1;
xfree(abs_dst);
abs_dst = NULL;
@@ -529,7 +530,7 @@ process_get(struct sftp_conn *conn, char
out:
xfree(abs_src);
- globfree(&g);
+ xglobfree(&g);
return(err);
}
@@ -539,7 +540,7 @@ process_put(struct sftp_conn *conn, char
char *tmp_dst = NULL;
char *abs_dst = NULL;
char *tmp;
- glob_t g;
+ xglob_t g;
int err = 0;
int i;
struct stat sb;
@@ -551,7 +552,7 @@ process_put(struct sftp_conn *conn, char
memset(&g, 0, sizeof(g));
debug3("Looking up %s", src);
- if (glob(src, GLOB_NOCHECK, NULL, &g)) {
+ if (xglob(src, XGLOB_NOCHECK, NULL, &g)) {
error("File \"%s\" not found.", src);
err = -1;
goto out;
@@ -565,19 +566,20 @@ process_put(struct sftp_conn *conn, char
goto out;
}
- for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
- if (stat(g.gl_pathv[i], &sb) == -1) {
+ for (i = 0; g.gl_matchv[i].gl_path && !interrupted; i++) {
+ if (stat(g.gl_matchv[i].gl_path, &sb) == -1) {
err = -1;
- error("stat %s: %s", g.gl_pathv[i], strerror(errno));
+ error("stat %s: %s", g.gl_matchv[i].gl_path,
+ strerror(errno));
continue;
}
if (!S_ISREG(sb.st_mode)) {
error("skipping non-regular file %s",
- g.gl_pathv[i]);
+ g.gl_matchv[i].gl_path);
continue;
}
- if (infer_path(g.gl_pathv[i], &tmp)) {
+ if (infer_path(g.gl_matchv[i].gl_path, &tmp)) {
err = -1;
goto out;
}
@@ -585,7 +587,7 @@ process_put(struct sftp_conn *conn, char
if (g.gl_matchc == 1 && tmp_dst) {
/* If directory specified, append filename */
if (remote_is_dir(conn, tmp_dst)) {
- if (infer_path(g.gl_pathv[0], &tmp)) {
+ if (infer_path(g.gl_matchv[0].gl_path, &tmp)) {
err = 1;
goto out;
}
@@ -600,8 +602,9 @@ process_put(struct sftp_conn *conn, char
} else
abs_dst = make_absolute(tmp, pwd);
- printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
- if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
+ printf("Uploading %s to %s\n", g.gl_matchv[i].gl_path, abs_dst);
+ if (do_upload(conn, g.gl_matchv[i].gl_path, abs_dst,
+ pflag) == -1)
err = -1;
}
@@ -610,7 +613,7 @@ out:
xfree(abs_dst);
if (tmp_dst)
xfree(tmp_dst);
- globfree(&g);
+ xglobfree(&g);
return(err);
}
@@ -716,21 +719,50 @@ do_ls_dir(struct sftp_conn *conn, char *
return (0);
}
+static int
+xglobentry_comp(const void *aa, const void *bb)
+{
+ struct xglob_entry *a = (struct xglob_entry *)aa;
+ struct xglob_entry *b = (struct xglob_entry *)bb;
+ int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
+
+#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
+ if (sort_flag & LS_NAME_SORT)
+ return (rmul * strcmp(a->gl_path, b->gl_path));
+ else if (sort_flag & LS_TIME_SORT) {
+ if (timespeccmp(&a->gl_stat.st_mtimespec,
+ &b->gl_stat.st_mtimespec, >))
+ return rmul;
+ if (timespeccmp(&a->gl_stat.st_mtimespec,
+ &b->gl_stat.st_mtimespec, <))
+ return -rmul;
+ return 0;
+ } else if (sort_flag & LS_SIZE_SORT)
+ return (rmul * NCMP(a->gl_stat.st_size, b->gl_stat.st_size));
+
+ fatal("Unknown ls sort type");
+}
+
/* sftp ls.1 replacement which handles path globs */
static int
do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
int lflag)
{
- glob_t g;
+ xglob_t g;
u_int i, c = 1, colspace = 0, columns = 1;
Attrib *a = NULL;
+ int err;
memset(&g, 0, sizeof(g));
- if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
+ g.gl_cmp = xglobentry_comp;
+ sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
+
+ if (remote_xglob(conn, path,
+ XGLOB_MARK|XGLOB_NOCHECK|XGLOB_BRACE|XGLOB_ALTCMPFUNC,
NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
if (g.gl_pathc)
- globfree(&g);
+ xglobfree(&g);
error("Can't ls: \"%s\" not found", path);
return (-1);
}
@@ -739,20 +771,19 @@ do_globbed_ls(struct sftp_conn *conn, ch
goto out;
/*
- * If the glob returns a single match and it is a directory,
+ * If the xglob returns a single match and it is a directory,
* then just list its contents.
*/
if (g.gl_matchc == 1) {
- if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
- globfree(&g);
+ if ((a = do_lstat(conn, g.gl_matchv[0].gl_path, 1)) == NULL) {
+ xglobfree(&g);
return (-1);
}
if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
S_ISDIR(a->perm)) {
- int err;
-
- err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
- globfree(&g);
+ err = do_ls_dir(conn, g.gl_matchv[0].gl_path,
+ strip_path, lflag);
+ xglobfree(&g);
return (err);
}
}
@@ -762,8 +793,8 @@ do_globbed_ls(struct sftp_conn *conn, ch
struct winsize ws;
/* Count entries for sort and find longest filename */
- for (i = 0; g.gl_pathv[i]; i++)
- m = MAX(m, strlen(g.gl_pathv[i]));
+ for (i = 0; g.gl_matchv[i].gl_path; i++)
+ m = MAX(m, strlen(g.gl_matchv[i].gl_path));
if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
width = ws.ws_col;
@@ -773,28 +804,12 @@ do_globbed_ls(struct sftp_conn *conn, ch
colspace = width / columns;
}
- for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
- char *fname;
-
- fname = path_strip(g.gl_pathv[i], strip_path);
+ for (i = 0; g.gl_matchv[i].gl_path && !interrupted; i++) {
+ char *fname, *lname;
+ fname = path_strip(g.gl_matchv[i].gl_path, strip_path);
if (lflag & LS_LONG_VIEW) {
- char *lname;
- struct stat sb;
-
- /*
- * XXX: this is slow - 1 roundtrip per path
- * A solution to this is to fork glob() and
- * build a sftp specific version which keeps the
- * attribs (which currently get thrown away)
- * that the server returns as well as the filenames.
- */
- memset(&sb, 0, sizeof(sb));
- if (a == NULL)
- a = do_lstat(conn, g.gl_pathv[i], 1);
- if (a != NULL)
- attrib_to_stat(a, &sb);
- lname = ls_file(fname, &sb, 1);
+ lname = ls_file(fname, &g.gl_matchv[i].gl_stat, 1);
printf("%s\n", lname);
xfree(lname);
} else {
@@ -813,7 +828,7 @@ do_globbed_ls(struct sftp_conn *conn, ch
out:
if (g.gl_pathc)
- globfree(&g);
+ xglobfree(&g);
return (0);
}
@@ -1218,7 +1233,7 @@ parse_dispatch_command(struct sftp_conn
Attrib a, *aa;
char path_buf[MAXPATHLEN];
int err = 0;
- glob_t g;
+ xglob_t g;
path1 = path2 = NULL;
cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg,
@@ -1255,10 +1270,10 @@ parse_dispatch_command(struct sftp_conn
break;
case I_RM:
path1 = make_absolute(path1, *pwd);
- remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
- for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
- printf("Removing %s\n", g.gl_pathv[i]);
- err = do_rm(conn, g.gl_pathv[i]);
+ remote_xglob(conn, path1, XGLOB_NOCHECK, NULL, &g);
+ for (i = 0; g.gl_matchv[i].gl_path && !interrupted; i++) {
+ printf("Removing %s\n", g.gl_matchv[i].gl_path);
+ err = do_rm(conn, g.gl_matchv[i].gl_path);
if (err != 0 && err_abort)
break;
}
@@ -1351,10 +1366,10 @@ parse_dispatch_command(struct sftp_conn
attrib_clear(&a);
a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
a.perm = n_arg;
- remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
- for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
- printf("Changing mode on %s\n", g.gl_pathv[i]);
- err = do_setstat(conn, g.gl_pathv[i], &a);
+ remote_xglob(conn, path1, XGLOB_NOCHECK, NULL, &g);
+ for (i = 0; g.gl_matchv[i].gl_path && !interrupted; i++) {
+ printf("Changing mode on %s\n", g.gl_matchv[i].gl_path);
+ err = do_setstat(conn, g.gl_matchv[i].gl_path, &a);
if (err != 0 && err_abort)
break;
}
@@ -1362,9 +1377,9 @@ parse_dispatch_command(struct sftp_conn
case I_CHOWN:
case I_CHGRP:
path1 = make_absolute(path1, *pwd);
- remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
- for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
- if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
+ remote_xglob(conn, path1, XGLOB_NOCHECK, NULL, &g);
+ for (i = 0; g.gl_matchv[i].gl_path && !interrupted; i++) {
+ if (!(aa = do_stat(conn, g.gl_matchv[i].gl_path, 0))) {
if (err != 0 && err_abort)
break;
else
@@ -1372,7 +1387,8 @@ parse_dispatch_command(struct sftp_conn
}
if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
error("Can't get current ownership of "
- "remote file \"%s\"", g.gl_pathv[i]);
+ "remote file \"%s\"",
+ g.gl_matchv[i].gl_path);
if (err != 0 && err_abort)
break;
else
@@ -1380,13 +1396,15 @@ parse_dispatch_command(struct sftp_conn
}
aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
if (cmdnum == I_CHOWN) {
- printf("Changing owner on %s\n", g.gl_pathv[i]);
+ printf("Changing owner on %s\n",
+ g.gl_matchv[i].gl_path);
aa->uid = n_arg;
} else {
- printf("Changing group on %s\n", g.gl_pathv[i]);
+ printf("Changing group on %s\n",
+ g.gl_matchv[i].gl_path);
aa->gid = n_arg;
}
- err = do_setstat(conn, g.gl_pathv[i], aa);
+ err = do_setstat(conn, g.gl_matchv[i].gl_path, aa);
if (err != 0 && err_abort)
break;
}
@@ -1423,7 +1441,7 @@ parse_dispatch_command(struct sftp_conn
}
if (g.gl_pathc)
- globfree(&g);
+ xglobfree(&g);
if (path1)
xfree(path1);
if (path2)
Index: xglob.c
===================================================================
RCS file: xglob.c
diff -N xglob.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ xglob.c 21 Jul 2008 13:11:46 -0000
@@ -0,0 +1,835 @@
+/* $OpenBSD$ */
+/*
+ * Copyright 2007 Damien Miller <djm at mindrot.org>
+ * 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.
+ * 3. 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.
+ */
+
+/*
+ * Modified version of BSD glob(3):
+ * - ALTDIRFUNCs gain context arguments. sftp uses this to pass in a
+ * structure representing the connection.
+ * - Instead of returning just a vector of paths, xglob returns a struct
+ * that contains the path and its stuct stat. This is useful for sftp, as
+ * it saves a round-trip later.
+ * - Added ALTCMPFUNC for caller-specified comparison functions to be passed
+ * to the final qsort(3).
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "xglob.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 path_compare(const void *, const void *);
+static int g_Ctoc(const Char *, char *, u_int);
+static int g_lstat(Char *, struct stat *, xglob_t *);
+static DIR *g_opendir(Char *, xglob_t *);
+static Char *g_strchr(Char *, int);
+static int g_stat(Char *, struct stat *, xglob_t *);
+static int xglob0(const Char *, xglob_t *);
+static int xglob1(Char *, Char *, xglob_t *, size_t *);
+static int xglob2(Char *, Char *, Char *, Char *, Char *, Char *,
+ xglob_t *, size_t *);
+static int xglob3(Char *, Char *, Char *, Char *, Char *,
+ Char *, Char *, xglob_t *, size_t *);
+static int xglobextend(const Char *, struct stat *, xglob_t *, size_t *);
+static const Char *
+ xglobtilde(const Char *, Char *, size_t, xglob_t *);
+static int xglobexp1(const Char *, xglob_t *);
+static int xglobexp2(const Char *, const Char *, xglob_t *, int *);
+static int match(Char *, Char *, Char *);
+#ifdef DEBUG
+static void qprintf(const char *, Char *);
+#endif
+
+int
+xglob(const char *pattern, int flags, int (*errfunc)(const char *, int),
+ xglob_t *pglob)
+{
+ const u_char *patnext;
+ int c;
+ Char *bufnext, *bufend, patbuf[MAXPATHLEN];
+
+ patnext = (u_char *) pattern;
+ if (!(flags & XGLOB_APPEND)) {
+ pglob->gl_pathc = 0;
+ pglob->gl_matchv = NULL;
+ if (!(flags & XGLOB_DOOFFS))
+ pglob->gl_offs = 0;
+ }
+ pglob->gl_flags = flags & ~XGLOB_MAGCHAR;
+ pglob->gl_errfunc = errfunc;
+ pglob->gl_matchc = 0;
+
+ bufnext = patbuf;
+ bufend = bufnext + MAXPATHLEN - 1;
+ if (flags & XGLOB_NOESCAPE)
+ while (bufnext < bufend && (c = *patnext++) != EOS)
+ *bufnext++ = c;
+ else {
+ /* 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;
+ }
+ *bufnext = EOS;
+
+ if (flags & XGLOB_BRACE)
+ return xglobexp1(patbuf, pglob);
+ else
+ return xglob0(patbuf, pglob);
+}
+
+/*
+ * 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
+xglobexp1(const Char *pattern, xglob_t *pglob)
+{
+ 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 xglob0(pattern, pglob);
+
+ while ((ptr = (const Char *) g_strchr((Char *) ptr, LBRACE)) != NULL)
+ if (!xglobexp2(ptr, pattern, pglob, &rv))
+ return rv;
+
+ return xglob0(pattern, pglob);
+}
+
+
+/*
+ * Recursive brace globbing helper. Tries to expand a single brace.
+ * If it succeeds then it invokes xglobexp1 with the new pattern.
+ * If it fails then it tries to glob the rest of the pattern and returns.
+ */
+static int
+xglobexp2(const Char *ptr, const Char *pattern, xglob_t *pglob, int *rv)
+{
+ 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++)
+ ;
+ *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++)
+ ;
+ 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 = xglob0(patbuf, pglob);
+ 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++)
+ ;
+ 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++)
+ ;
+
+ /*
+ * Append the rest of the pattern after the
+ * closing brace
+ */
+ for (pl = pe + 1; (*lm++ = *pl++) != EOS; )
+ ;
+
+ /* Expand the current pattern */
+#ifdef DEBUG
+ qprintf("xglobexp2:", patbuf);
+#endif
+ *rv = xglobexp1(patbuf, pglob);
+
+ /* move after the comma, to the next string */
+ pl = pm + 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ *rv = 0;
+ return 0;
+}
+
+
+
+/*
+ * expand tilde from the passwd file.
+ */
+static const Char *
+xglobtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, xglob_t *pglob)
+{
+ struct passwd *pwd;
+ char *h;
+ const Char *p;
+ Char *b, *eb;
+
+ if (*pattern != TILDE || !(pglob->gl_flags & XGLOB_TILDE))
+ return pattern;
+
+ /* Copy up to the end of the string or / */
+ eb = &patbuf[patbuf_len - 1];
+ for (p = pattern + 1, h = (char *) patbuf;
+ h < (char *)eb && *p && *p != SLASH; *h++ = *p++)
+ ;
+
+ *h = EOS;
+
+#if 0
+ if (h == (char *)eb)
+ return what;
+#endif
+
+ if (((char *) patbuf)[0] == EOS) {
+ /*
+ * handle a plain ~ or ~/ by expanding $HOME
+ * first and then trying the password file
+ */
+ if (issetugid() != 0 || (h = getenv("HOME")) == NULL) {
+ if ((pwd = getpwuid(getuid())) == NULL)
+ return pattern;
+ else
+ h = pwd->pw_dir;
+ }
+ } else {
+ /*
+ * Expand a ~user
+ */
+ if ((pwd = getpwnam((char*) patbuf)) == NULL)
+ return pattern;
+ else
+ h = pwd->pw_dir;
+ }
+
+ /* Copy the home directory */
+ for (b = patbuf; b < eb && *h; *b++ = *h++)
+ ;
+
+ /* Append the rest of the pattern */
+ while (b < eb && (*b++ = *p++) != EOS)
+ ;
+ *b = EOS;
+
+ return patbuf;
+}
+
+
+/*
+ * The main xglob() routine: compiles the pattern (optionally processing
+ * quotes), calls xglob1() 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
+xglob0(const Char *pattern, xglob_t *pglob)
+{
+ const Char *qpatnext;
+ int c, err, oldpathc;
+ Char *bufnext, patbuf[MAXPATHLEN];
+ size_t limit = 0;
+
+ qpatnext = xglobtilde(pattern, patbuf, MAXPATHLEN, pglob);
+ 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 |= XGLOB_MAGCHAR;
+ *bufnext++ = M_END;
+ break;
+ case QUESTION:
+ pglob->gl_flags |= XGLOB_MAGCHAR;
+ *bufnext++ = M_ONE;
+ break;
+ case STAR:
+ pglob->gl_flags |= XGLOB_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("xglob0:", patbuf);
+#endif
+
+ if ((err = xglob1(patbuf, patbuf+MAXPATHLEN-1, pglob, &limit)) != 0)
+ return(err);
+
+ /*
+ * If there was no match we are going to append the pattern
+ * if XGLOB_NOCHECK was specified or if XGLOB_NOMAGIC was specified
+ * and the pattern did not contain any magic characters
+ * XGLOB_NOMAGIC is there just for compatibility with csh.
+ */
+ if (pglob->gl_pathc == oldpathc) {
+ if ((pglob->gl_flags & XGLOB_NOCHECK) ||
+ ((pglob->gl_flags & XGLOB_NOMAGIC) &&
+ !(pglob->gl_flags & XGLOB_MAGCHAR)))
+ return(xglobextend(pattern, NULL, pglob, &limit));
+ else
+ return(XGLOB_NOMATCH);
+ }
+ if (!(pglob->gl_flags & XGLOB_NOSORT))
+ qsort(pglob->gl_matchv + pglob->gl_offs + oldpathc,
+ pglob->gl_pathc - oldpathc, sizeof(*pglob->gl_matchv),
+ (pglob->gl_flags & XGLOB_ALTCMPFUNC) ?
+ pglob->gl_cmp : path_compare);
+ return(0);
+}
+
+static int
+path_compare(const void *a, const void *b)
+{
+ struct xglob_entry *aa = (struct xglob_entry *)a;
+ struct xglob_entry *bb = (struct xglob_entry *)b;
+
+ return(strcmp(aa->gl_path, bb->gl_path));
+}
+
+static int
+xglob1(Char *pattern, Char *pattern_last, xglob_t *pglob, size_t *limitp)
+{
+ Char pathbuf[MAXPATHLEN];
+
+ /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
+ if (*pattern == EOS)
+ return(0);
+ return(xglob2(pathbuf, pathbuf+MAXPATHLEN-1,
+ pathbuf, pathbuf+MAXPATHLEN-1,
+ pattern, pattern_last, pglob, limitp));
+}
+
+/*
+ * The functions xglob2 and xglob3 are mutually recursive; there is one level
+ * of recursion for each segment in the pattern that contains one or more
+ * meta characters.
+ */
+static int
+xglob2(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last,
+ Char *pattern, Char *pattern_last, xglob_t *pglob, size_t *limitp)
+{
+ 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 & XGLOB_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(xglobextend(pathbuf, &sb, pglob, limitp));
+ }
+
+ /* 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(xglob3(pathbuf, pathbuf_last, pathend,
+ pathend_last, pattern, p, pattern_last,
+ pglob, limitp));
+ }
+ /* NOTREACHED */
+}
+
+static int
+xglob3(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last,
+ Char *pattern, Char *restpattern, Char *restpattern_last, xglob_t *pglob,
+ size_t *limitp)
+{
+ struct dirent *dp;
+ DIR *dirp;
+ int err;
+ char buf[MAXPATHLEN];
+
+ 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(XGLOB_ABORTED);
+ if (pglob->gl_errfunc(buf, errno) ||
+ pglob->gl_flags & XGLOB_ERR)
+ return(XGLOB_ABORTED);
+ }
+ return(0);
+ }
+
+ err = 0;
+
+ /* Search directory for matching names. */
+ while (1) {
+ if (pglob->gl_flags & XGLOB_ALTDIRFUNC)
+ dp = pglob->gl_readdir(pglob->gl_user_ctx, dirp);
+ else
+ dp = readdir(dirp);
+
+ if (dp == NULL)
+ break;
+
+ 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 (dc >= pathend_last) {
+ *dc = EOS;
+ err = 1;
+ break;
+ }
+
+ if (!match(pathend, pattern, restpattern)) {
+ *pathend = EOS;
+ continue;
+ }
+ err = xglob2(pathbuf, pathbuf_last, --dc, pathend_last,
+ restpattern, restpattern_last, pglob, limitp);
+ if (err)
+ break;
+ }
+
+ if (pglob->gl_flags & XGLOB_ALTDIRFUNC)
+ (*pglob->gl_closedir)(pglob->gl_user_ctx, dirp);
+ else
+ closedir(dirp);
+ return(err);
+}
+
+
+/*
+ * Extend the gl_matchv member of a xglob_t structure to accommodate 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 xglob_t structure:
+ * Either gl_pathc is zero and gl_matchv is NULL; or gl_pathc > 0 and
+ * gl_matchv points to (gl_offs + gl_pathc + 1) items.
+ */
+static int
+xglobextend(const Char *path, struct stat *st, xglob_t *pglob, size_t *limitp)
+{
+ struct xglob_entry *matchv;
+ size_t newsize, len, i;
+ char *copy;
+ const Char *p;
+
+ newsize = sizeof(*matchv) * (2 + pglob->gl_pathc + pglob->gl_offs);
+ matchv = realloc(pglob->gl_matchv, newsize);
+ if (matchv == NULL) {
+ if (pglob->gl_matchv) {
+ free(pglob->gl_matchv);
+ pglob->gl_matchv = NULL;
+ }
+ return(XGLOB_NOSPACE);
+ }
+
+ if (pglob->gl_matchv == NULL && pglob->gl_offs > 0)
+ memset(matchv, '\0', newsize);
+
+ pglob->gl_matchv = matchv;
+
+ for (p = path; *p++;)
+ ;
+ len = (size_t)(p - path);
+ *limitp += len;
+ copy = malloc(len);
+ if (copy != NULL) {
+ if (g_Ctoc(path, copy, len)) {
+ free(copy);
+ copy = NULL;
+ }
+ i = pglob->gl_offs + pglob->gl_pathc++;
+ matchv[i].gl_path = copy;
+ if (st != NULL)
+ matchv[i].gl_stat = *st;
+ }
+ memset(&matchv[pglob->gl_offs + pglob->gl_pathc], '\0',
+ sizeof(*matchv));
+
+ if ((pglob->gl_flags & XGLOB_LIMIT) &&
+ newsize + *limitp >= ARG_MAX) {
+ errno = 0;
+ return(XGLOB_NOSPACE);
+ }
+
+ return(copy == NULL ? XGLOB_NOSPACE : 0);
+}
+
+
+/*
+ * pattern matching function for filenames. Each occurrence of the *
+ * pattern causes a recursion level.
+ */
+static int
+match(Char *name, Char *pat, Char *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 (c <= k && k <= pat[1])
+ 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 xglob_t structure. */
+void
+xglobfree(xglob_t *pglob)
+{
+ int i;
+ struct xglob_entry *pp;
+
+ if (pglob->gl_matchv != NULL) {
+ pp = pglob->gl_matchv + pglob->gl_offs;
+ for (i = pglob->gl_pathc; i--; ++pp)
+ if (pp->gl_path)
+ free(pp->gl_path);
+ free(pglob->gl_matchv);
+ pglob->gl_matchv = NULL;
+ }
+}
+
+static DIR *
+g_opendir(Char *str, xglob_t *pglob)
+{
+ char buf[MAXPATHLEN];
+
+ if (!*str)
+ strlcpy(buf, ".", sizeof buf);
+ else {
+ if (g_Ctoc(str, buf, sizeof(buf)))
+ return(NULL);
+ }
+
+ if (pglob->gl_flags & XGLOB_ALTDIRFUNC)
+ return((*pglob->gl_opendir)(pglob->gl_user_ctx, buf));
+
+ return(opendir(buf));
+}
+
+static int
+g_lstat(Char *fn, struct stat *sb, xglob_t *pglob)
+{
+ char buf[MAXPATHLEN];
+
+ if (g_Ctoc(fn, buf, sizeof(buf)))
+ return(-1);
+ if (pglob->gl_flags & XGLOB_ALTDIRFUNC)
+ return((*pglob->gl_lstat)(pglob->gl_user_ctx, buf, sb));
+ return(lstat(buf, sb));
+}
+
+static int
+g_stat(Char *fn, struct stat *sb, xglob_t *pglob)
+{
+ char buf[MAXPATHLEN];
+
+ if (g_Ctoc(fn, buf, sizeof(buf)))
+ return(-1);
+ if (pglob->gl_flags & XGLOB_ALTDIRFUNC)
+ return((*pglob->gl_stat)(pglob->gl_user_ctx, buf, sb));
+ return(stat(buf, sb));
+}
+
+static Char *
+g_strchr(Char *str, int ch)
+{
+ do {
+ if (*str == ch)
+ return (str);
+ } while (*str++);
+ return (NULL);
+}
+
+static int
+g_Ctoc(const Char *str, char *buf, u_int len)
+{
+
+ while (len--) {
+ if ((*buf++ = *str++) == EOS)
+ return (0);
+ }
+ return (1);
+}
+
+#ifdef DEBUG
+static void
+qprintf(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
Index: xglob.h
===================================================================
RCS file: xglob.h
diff -N xglob.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ xglob.h 21 Jul 2008 13:11:46 -0000
@@ -0,0 +1,108 @@
+/* $OpenBSD: glob.h,v 1.10 2005/12/13 00:35:22 millert Exp $ */
+/* $NetBSD: glob.h,v 1.5 1994/10/26 00:55:56 cgd Exp $ */
+
+/*
+ * 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.
+ * 3. 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.
+ *
+ * @(#)glob.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _XGLOB_H_
+#define _XGLOB_H_
+
+#include <sys/cdefs.h>
+
+struct xglob_entry {
+ char *gl_path; /* Path name */
+ struct stat gl_stat; /* Corresponding struct stat */
+};
+
+struct stat;
+typedef struct {
+ int gl_pathc; /* Count of total paths so far. */
+ int gl_matchc; /* Count of paths matching pattern. */
+ int gl_offs; /* Reserved at beginning of gl_matchv. */
+ int gl_flags; /* Copy of flags parameter to glob. */
+
+ struct xglob_entry *gl_matchv;
+ /* List of entries matching */
+
+ void *gl_user_ctx; /* User context, also passed to ALTDIRFUNCs */
+
+ int (*gl_errfunc)(const char *, int);
+
+ /*
+ * Alternate filesystem access methods for glob; replacement
+ * versions of closedir(3), readdir(3), opendir(3), stat(2)
+ * and lstat(2). These versions are passed xglob_t->gl_user_ctx
+ * as their first arguments.
+ */
+ void (*gl_closedir)(void *, void *);
+ struct dirent *(*gl_readdir)(void *, void *);
+ void *(*gl_opendir)(void *, const char *);
+ int (*gl_lstat)(void *, const char *, struct stat *);
+ int (*gl_stat)(void *, const char *, struct stat *);
+
+ /*
+ * Alternate comparison function, used to qsort(3) gl_matchv.
+ * if the XGLOB_ALTCMPFUNC option has been specified.
+ * Arguments to the comparison function are both struct glob_entry *
+ */
+ int (*gl_cmp)(const void *, const void *);
+} xglob_t;
+
+#define XGLOB_APPEND 0x0001 /* Append to output from previous call. */
+#define XGLOB_DOOFFS 0x0002 /* Use gl_offs. */
+#define XGLOB_ERR 0x0004 /* Return on error. */
+#define XGLOB_MARK 0x0008 /* Append / to matching directories. */
+#define XGLOB_NOCHECK 0x0010 /* Return pattern itself if nothing matches. */
+#define XGLOB_NOSORT 0x0020 /* Don't sort. */
+#define XGLOB_NOESCAPE 0x1000 /* Disable backslash escaping. */
+
+#define XGLOB_NOSPACE (-1) /* Malloc call failed. */
+#define XGLOB_ABORTED (-2) /* Unignored error. */
+#define XGLOB_NOMATCH (-3) /* No match and GLOB_NOCHECK not set. */
+#define XGLOB_NOSYS (-4) /* Function not supported. */
+
+#define XGLOB_ALTDIRFUNC 0x0040 /* Use alternately specified directory funcs. */
+#define XGLOB_BRACE 0x0080 /* Expand braces ala csh. */
+#define XGLOB_MAGCHAR 0x0100 /* Pattern had globbing characters. */
+#define XGLOB_NOMAGIC 0x0200 /* GLOB_NOCHECK without magic chars (csh). */
+#define XGLOB_QUOTE 0x0400 /* Quote special chars with \. */
+#define XGLOB_TILDE 0x0800 /* Expand tilde names from the passwd file. */
+#define XGLOB_LIMIT 0x2000 /* Limit pattern match output to ARG_MAX */
+#define XGLOB_ALTCMPFUNC 0x4000 /* Use alternately specified comparison func */
+#define XGLOB_ABEND GLOB_ABORTED /* backward compatibility */
+
+int xglob(const char *, int, int (*)(const char *, int), xglob_t *);
+void xglobfree(xglob_t *);
+
+#endif /* !_XGLOB_H_ */
Index: sftp/Makefile
===================================================================
RCS file: /cvs/src/usr.bin/ssh/sftp/Makefile,v
retrieving revision 1.11
diff -u -p -r1.11 Makefile
--- sftp/Makefile 18 Apr 2008 12:32:11 -0000 1.11
+++ sftp/Makefile 21 Jul 2008 13:11:46 -0000
@@ -10,7 +10,7 @@ BINMODE?=555
BINDIR= /usr/bin
MAN= sftp.1
-SRCS= sftp.c sftp-client.c sftp-common.c sftp-glob.c
+SRCS= sftp.c sftp-client.c sftp-common.c xglob.c sftp-glob.c
.include <bsd.prog.mk>
More information about the openssh-unix-dev
mailing list