On Mon, Dec 10, 2001 at 01:31:09PM +0300, Alexander V. Lukyanov wrote:
> This is because url_session is not given with foreground priority.
> Since you have replaced rm, I'll not bother with fixing it, only mkdir.
That didn't fix it, though. Both cases still stall for a second for me.
>> I've finally managed to get FinderJob working the way I've been
>> trying.
>> (It needs more testing before I send a patch, and I'll wait for you to
>> finish with the last one first anyway.)
>
> Nice. Send the patch.
I'll send what I have. Still no URL support (or redirection). The
below problem is more important, too.
>> rm and chmod no longer work with urls. I'll try to add URL support to
>> FinderJob.
>
> Probably it should handle redirections too.
You'll probably notice "find" alone prefixes "."; this is due to
FinderJob being generalized a bit more, and it happens to match
/usr/bin/find's behavior, so I left it.
As for URLs, there's two implementation options:
> find ftp://host/path/
ftp://host/path/
ftp://host/path/file
ftp://host/path/dir/
ftp://host/path/dir/file
or just the paths. This has the advantage of being able to produce
lists of full URLs, and is consistent with showing the whole path as
given by the user.
I changed the way GetFileInfo finds the path to verify: it Chdir()s to
the full path (without validation), grabs the basename of the CWD, then
Chdir("..")s (with validation). (This is only for the "try the path as
a file" case.) This fixes GetFileInfo paths ending with "." or ".."
(ie "cls -ld ..".) It simply lets FileAccess handle paths (including
relative paths) so it doesn't have to. Since we do another Chdir
immediately afterwards, the initial one is never actually sent.
It still can't handle ~, however. That is, "find ~" doesn't work; and "find ."
doesn't work when the CWD contains a ~. (This is a problem--"find" alone
doesn't work before you're connected.) We probably need to connect to find
out the home path (if it's not already known), and we probably need to do
something like ExpandTildeInCWD in GetFileInfo after the full path Chdir.
I'm not sure how to tell a FileAccess to simply find out the home path; some
state mode of "expand tildes in the cwd, connecting if necessary" would do
it. We need to make sure that extra "test" Chdir above never actually
gets sent, though. (The path it Chdirs into may actually be a file,
which is OK since we Chdir out of it immediately and never let it get
sent.)
Changes:
acconfig.h, m4/lftp.m4: Move BOOL config.h code into m4.
lib/Makefile.am, include/Makefile.am: add modechange.{c,h}.
ChmodJob.cc, ChmodJob.h: Add relative paths, -v, -c, -R. Maintain
simple numeric mode interface.
FileSet.cc: sane default for mode when we don't have it defined.
FileSetOutput.cc: make sure "cls -ld ." shows the directory, even
without "show_dots".
FindJob.cc, FindJob.h:
Accept files as well as directories to NextDir().
Pass top-level path to ProcessFile.
Don't get listing if we don't need it.
FindJobDu.cc: print files with "du filename" even without -a.
GetFileInfo.cc, GetFileInfo.h:
Make prefixing the path optional.
Add member to find out whether the given path was a file or directory.
Fix verification with ".." at the end of path.
If ".." was requested (with show_dirs on), return "..", not the
name of the directory, etc.
TreatFileJob.cc, TreatFileJob.h: Derive from FinderJob. AddFile() no
longer needed.
commands.cc: chmod, rm, rmdir updated. Give find "." instead of "" by
default.
misc.cc, misc.h: Add dirname_alloc(). (Semantics are not exactly the
same as dirname.)
Uncertain about this change, remove if needed:
Treat "~" and "~/..." as absolute paths. (Makes "find ~/path"
work. "find ~" still won't.)
rmJob.cc, rmJob.h: Handle rm -r directly.
--
Glenn Maynard
? gettext.diff.merge
? gettext.diff2
? gettext.diff3
? trans_quotes.pl
? include/modechange.h
? lib/modechange.c
? src/.FindJob.cc.swp
? src/.GetFileInfo.cc.swp
? src/.LsCache.cc.swp
? src/.commands.cc.swp
? src/.mkdirJob.cc.swp
? src/cs
? src/d
? src/find
? src/globbed
? src/gmon.out
? src/lsc
? src/stG65e2D
? src/test
Index: acconfig.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/acconfig.h,v
retrieving revision 1.29
diff -u -r1.29 acconfig.h
--- acconfig.h 2001/12/05 17:11:06 1.29
+++ acconfig.h 2001/12/10 19:48:19
@@ -250,13 +250,6 @@
# define __builtin_expect(expr, val) (expr)
#endif
-#if defined(__cplusplus) && !defined(HAVE_CXX_BOOL)
-typedef unsigned _bool;
-#define bool _bool
-#define false 0U
-#define true 1U
-#endif
-
#ifdef __GNUC__
# define PRINTF_LIKE(n,m) __attribute__((format(printf,n,m)))
#else
Index: include/Makefile.am
===================================================================
RCS file: /home/lav/cvsroot/lftp/include/Makefile.am,v
retrieving revision 1.7
diff -u -r1.7 Makefile.am
--- include/Makefile.am 2001/11/09 13:36:37 1.7
+++ include/Makefile.am 2001/12/10 19:48:20
@@ -1,5 +1,5 @@
TRIO_FILES=strio.h trio.h triop.h trionan.h triodef.h
noinst_HEADERS = xalloca.h getopt.h xstring.h filemode.h mbswidth.h \
- human.h xstrtol.h \
+ human.h xstrtol.h modechange.h \
$(TRIO_FILES)
DISTCLEANFILES = poll.h regex.h glob.h fnmatch.h readline libintl.h
Index: lib/Makefile.am
===================================================================
RCS file: /home/lav/cvsroot/lftp/lib/Makefile.am,v
retrieving revision 1.5
diff -u -r1.5 Makefile.am
--- lib/Makefile.am 2001/11/09 13:37:14 1.5
+++ lib/Makefile.am 2001/12/10 19:48:20
@@ -1,6 +1,6 @@
noinst_LIBRARIES = liblib.a
liblib_a_SOURCES = poll.h glob.h fnmatch.h regex.h filemode.c mbswidth.c \
- human.c xstrtol.c xstrtoul.c
+ human.c xstrtol.c xstrtoul.c modechange.c
liblib_a_LIBADD = @LIBOBJS@ @ALLOCA@
INCLUDES = -I$(top_srcdir)/include
EXTRA_DIST = fnmatch_loop.c
Index: m4/lftp.m4
===================================================================
RCS file: /home/lav/cvsroot/lftp/m4/lftp.m4,v
retrieving revision 1.4
diff -u -r1.4 lftp.m4
--- m4/lftp.m4 2001/10/23 07:58:43 1.4
+++ m4/lftp.m4 2001/12/10 19:48:21
@@ -200,4 +200,11 @@
if test x$lftp_cv_cxx_bool = xyes; then
AC_DEFINE(HAVE_CXX_BOOL, 1, [define if c++ compiler supports bool])
fi
+ AH_VERBATIM([OPT_CPP_BOOL], [
+#if defined(__cplusplus) && !defined(HAVE_CXX_BOOL)
+ typedef unsigned _bool;
+#define bool _bool
+#define false 0U
+#define true 1U
+#endif ])
])
Index: src/ChmodJob.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/ChmodJob.cc,v
retrieving revision 1.2
diff -u -r1.2 ChmodJob.cc
--- src/ChmodJob.cc 2000/01/22 16:27:28 1.2
+++ src/ChmodJob.cc 2001/12/10 19:48:23
@@ -21,21 +21,104 @@
/* $Id: ChmodJob.cc,v 1.2 2000/01/22 16:27:28 lav Exp $ */
#include <config.h>
+#include <assert.h>
#include "ChmodJob.h"
#include "url.h"
+#include "filemode.h"
-ChmodJob::ChmodJob(FileAccess *s,int new_m,ArgV *a) : TreatFileJob(s,a)
+void ChmodJob::Init()
{
- m=new_m;
+ verbose=V_NONE;
+ m=0;
+ simple_mode=-1;
}
-void ChmodJob::TreatCurrent()
+ChmodJob::ChmodJob(FileAccess *s,ArgV *a) : TreatFileJob(s,a)
{
- ParsedURL u(curr,true);
- if(u.proto)
- url_session=FA::New(&u);
- if(url_session)
- url_session->Chmod(u.path,m);
+ Init();
+}
+
+ChmodJob::ChmodJob(FileAccess *s,int mode,ArgV *a) : TreatFileJob(s,a)
+{
+ Init();
+ simple_mode=mode;
+}
+
+ChmodJob::~ChmodJob()
+{
+ mode_free(m);
+}
+
+void ChmodJob::Recurse()
+{
+ set_maxdepth(-1);
+}
+
+int ChmodJob::GetMode(const FileInfo *fi) const
+{
+ if(simple_mode != -1)
+ return simple_mode;
+
+ return mode_adjust(fi->mode, m);
+}
+
+void ChmodJob::CurrentFinished(const char *d,const FileInfo *fi)
+{
+ const char *fmt;
+ if(session->Done() < 0)
+ {
+ if(quiet)
+ return;
+ fmt = _("failed to change mode of %s to %04lo (%s)\n");
+ }
else
- session->Chmod(curr,m);
+ fmt = _("mode of %s changed to %04lo (%s)\n");
+
+
+ unsigned mode = GetMode(fi);
+ if(verbose == V_ALL || (verbose == V_CHANGES && mode != fi->mode))
+ {
+ char perms[11]; /* "-rwxrwxrwx" ls-style modes. */
+
+ mode_string (mode, perms);
+ perms[10] = '\0'; /* `mode_string' does not null terminate. */
+
+ eprintf (fmt, fi->name, (unsigned long) mode, perms+1);
+ }
+}
+
+void ChmodJob::SetMode(mode_change *newm)
+{
+ m=newm;
+ /* request mode info only if we need it */
+ if(RelativeMode(m))
+ Need(FileInfo::MODE);
+
+ /* one or the other */
+ assert(simple_mode == -1);
+}
+
+void ChmodJob::SetVerbosity(verbosity v)
+{
+ verbose=v;
+
+ /* need file mode to show changes */
+ if(verbose == V_CHANGES)
+ Need(FileInfo::MODE);
+}
+
+bool ChmodJob::RelativeMode(const mode_change *m) const
+{
+ while(m)
+ {
+ if(m->flags || m->op != '=')
+ return true;
+ m=m->next;
+ }
+ return false;
+}
+
+void ChmodJob::TreatCurrent(const char *d,const FileInfo *fi)
+{
+ session->Chmod(fi->name,GetMode(fi));
}
Index: src/ChmodJob.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/ChmodJob.h,v
retrieving revision 1.1
diff -u -r1.1 ChmodJob.h
--- src/ChmodJob.h 1999/05/26 14:02:59 1.1
+++ src/ChmodJob.h 2001/12/10 19:48:23
@@ -24,15 +24,39 @@
#define CHMODJOB_H
#include "TreatFileJob.h"
+#include "FileSet.h"
+CDECL_BEGIN
+#include "modechange.h"
+CDECL_END
class ChmodJob : public TreatFileJob
{
- void TreatCurrent();
+public:
+ enum verbosity { V_NONE, V_CHANGES, V_ALL };
+
+private:
+ void TreatCurrent(const char *d,const FileInfo *fi);
+ void CurrentFinished(const char *d,const FileInfo *fi);
- int m;
+ void Init();
+ void Report(const char *d,const FileInfo *fi, bool success);
+ bool RelativeMode(const mode_change *m) const;
+ verbosity verbose;
+ mode_change *m;
+ int simple_mode;
+ int GetMode(const FileInfo *fi) const;
+
public:
- ChmodJob(FileAccess *s,int new_m,ArgV *a);
+ /* if you use this constructor, also set a mode with SetMode() */
+ ChmodJob(FileAccess *s,ArgV *a);
+ /* simple "chmod 123" interface: */
+ ChmodJob(FileAccess *s,int m,ArgV *a);
+ ChmodJob::~ChmodJob();
+
+ void SetVerbosity(verbosity v);
+ void SetMode(mode_change *newm);
+ void Recurse();
};
#endif//CHMODJOB_H
Index: src/FileAccess.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/FileAccess.cc,v
retrieving revision 1.89
diff -u -r1.89 FileAccess.cc
--- src/FileAccess.cc 2001/12/05 15:03:41 1.89
+++ src/FileAccess.cc 2001/12/10 19:48:24
@@ -33,6 +33,7 @@
#include <errno.h>
#include "ascii_ctype.h"
#include <fcntl.h>
+#include <fnmatch.h>
#include "LsCache.h"
#include "xalloca.h"
#include "log.h"
Index: src/FileAccess.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/FileAccess.h,v
retrieving revision 1.68
diff -u -r1.68 FileAccess.h
--- src/FileAccess.h 2001/12/05 16:37:13 1.68
+++ src/FileAccess.h 2001/12/10 19:48:25
@@ -331,6 +331,7 @@
priority=p;
current->Timeout(0);
}
+ int GetPriority() const { return priority; }
// not pretty (FIXME)
int GetRetries() { return retries; }
Index: src/FileSet.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/FileSet.cc,v
retrieving revision 1.30
diff -u -r1.30 FileSet.cc
--- src/FileSet.cc 2001/12/10 17:08:27 1.30
+++ src/FileSet.cc 2001/12/10 19:48:26
@@ -586,6 +586,7 @@
data=0;
user=0; group=0;
rank=0;
+ mode=0;
}
FileInfo::FileInfo(const FileInfo &fi)
Index: src/FileSetOutput.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/FileSetOutput.cc,v
retrieving revision 1.18
diff -u -r1.18 FileSetOutput.cc
--- src/FileSetOutput.cc 2001/12/05 17:12:51 1.18
+++ src/FileSetOutput.cc 2001/12/10 19:48:26
@@ -71,7 +71,8 @@
for(int i = 0; fs[i]; i++) {
const FileInfo *f = fs[i];
- if(!showdots && (!strcmp(basename_ptr(f->name),".") ||
!strcmp(basename_ptr(f->name),"..")))
+ if(!showdots && !list_directories &&
+ (!strcmp(basename_ptr(f->name),".") ||
+!strcmp(basename_ptr(f->name),"..")))
continue;
if(pat && *pat &&
Index: src/FindJob.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/FindJob.cc,v
retrieving revision 1.18
diff -u -r1.18 FindJob.cc
--- src/FindJob.cc 2001/11/09 13:36:27 1.18
+++ src/FindJob.cc 2001/12/10 19:48:27
@@ -20,6 +20,10 @@
/* $Id: FindJob.cc,v 1.18 2001/11/09 13:36:27 lav Exp $ */
+/* Ideally, this is simple:
+ *
+ * Queue FileInfo;
+ */
#include <config.h>
#include <assert.h>
#include <fnmatch.h>
@@ -27,58 +31,57 @@
#include "FindJob.h"
#include "CmdExec.h"
#include "misc.h"
+#include "GetFileInfo.h"
+#include "url.h"
#define top (*stack[stack_ptr])
+#define super SessionJob
int FinderJob::Do()
{
int m=STALL;
- int res;
prf_res pres;
Job *j;
switch(state)
{
- case INIT:
- // nothing to do - just fall to CD
-
- {
- const char *cd_dir=dir;
- if(stack_ptr>=0)
- cd_dir=dir_file(dir_file(start_dir,top.path),dir);
- session->Chdir(cd_dir,false);
- }
- state=CD;
- m=MOVED;
- case CD:
- res=session->Done();
- if(res==session->IN_PROGRESS)
- return m;
- if(res==session->OK)
- {
- session->Close();
- goto pre_INFO;
+ case START_INFO:
+ {
+ /* If we're not validating, and this is an argument (first-level path),
+ * pretend the file exists. */
+ if((file_info_need|FileInfo::NAME) == FileInfo::NAME &&
+ !validate_args && stack_ptr == -1)
+ {
+ FileInfo *fi = new FileInfo(dir);
+ FileSet *fs = new FileSet();
+ fs->Add(fi);
+ Push(fs);
+ state=LOOP;
+ return MOVED;
}
- // cd error
- if(!quiet)
- eprintf("%s: cd %s: %s\n",op,dir,session->StrError(res));
- errors++;
- depth_done=true;
- state=LOOP;
- return MOVED;
- pre_INFO:
- li=session->MakeListInfo();
- if(li==0)
- {
- //FIXME
- abort();
- }
- li->Need(file_info_need|FileInfo::NAME|FileInfo::TYPE);
+ /* The first time we get here (stack_ptr == -1), dir is an actual
+ * argument, so it might be a file. (Every other time, it's guaranteed
+ * to be a directory.) Set show_dirs to true, so it'll end up actually
+ * being on the stack, with type information. */
+ li=new GetFileInfo(session, dir, stack_ptr == -1);
+
+ /* Prepend for the argument level entry only: */
+ if(stack_ptr != -1)
+ li->DontPrependPath();
+
+ int need = file_info_need|FileInfo::NAME;
+
+ /* We only explicitely need the type if we're recursing further. */
+ if(stack_ptr+1 < maxdepth)
+ need |= FileInfo::TYPE;
+
+ li->Need(need);
if(use_cache)
li->UseCache();
state=INFO;
m=MOVED;
+ }
case INFO:
if(!li->Done())
return m;
@@ -89,15 +92,19 @@
Delete(li);
li=0;
errors++;
- depth_done=true;
+ contents_done=true;
state=LOOP;
return MOVED;
}
+
+ if(stack_ptr != -1 && li->WasDirectory())
+ Enter(dir);
+
Push(li->GetResult());
+ top.fset->rewind();
+
Delete(li);
li=0;
-
- top.fset->rewind();
state=LOOP;
m=MOVED;
case LOOP:
@@ -106,16 +113,17 @@
Up();
return MOVED;
}
+
+ session->Chdir(dir_file(init_dir, top.path),false);
// at this point either is true:
- // 1. we just process another file (!depth_done)
- // 2. we just returned from a subdir (depth_done)
- if(depth_first && !depth_done && (maxdepth == -1 || stack_ptr+1 < maxdepth))
+ // 1. we just process another file (!contents_done)
+ // 2. we just returned from a subdir (contents_done)
+ if(contents_first && !contents_done && (maxdepth == -1 || stack_ptr+1 <
+maxdepth))
{
FileInfo *f=top.fset->curr();
if((f->defined&f->TYPE) && f->filetype==f->DIRECTORY)
{
Down(f->name);
- Enter(f->name);
return MOVED;
}
}
@@ -124,7 +132,7 @@
if(pres==PRF_LATER)
return m;
- depth_done=false;
+ contents_done=false;
switch(pres)
{
@@ -147,14 +155,13 @@
post_WAIT:
if(stack_ptr==-1)
return m;
- if(!depth_first && (maxdepth == -1 || stack_ptr+1 < maxdepth))
+ if(!contents_first && (maxdepth == -1 || stack_ptr+1 < maxdepth))
{
FileInfo *f=top.fset->curr();
if((f->defined&f->TYPE) && f->filetype==f->DIRECTORY)
{
top.fset->next();
Down(f->name);
- Enter(f->name);
return MOVED;
}
}
@@ -186,11 +193,14 @@
Finish();
return;
}
- Exit();
+ /* stack[0] is the dir entry for the argument (ie. ls -d dir), and
+ * stack[1] is the contents (ls dir); don't exit for the first. */
+ if(stack_ptr)
+ Exit();
delete stack[stack_ptr--];
if(stack_ptr==-1)
goto done;
- depth_done=true;
+ contents_done=true;
state=LOOP;
}
@@ -207,11 +217,13 @@
stack=(place**)xrealloc(stack,sizeof(*stack)*stack_allocated);
}
+ if(stack_ptr != 0)
+ fset->ExcludeDots(); /* don't need . and .. (except for stack[0]) */
+
const char *new_path="";
if(old_path) // the first path will be empty
new_path=dir_file(old_path,dir);
- fset->ExcludeDots(); // don't need . and ..
/* matching exclusions don't include the path, so they operate
* on the filename portion only */
if(exclude)
@@ -236,8 +248,9 @@
void FinderJob::Down(const char *p)
{
- dir=p;
- state=INIT;
+ xfree(dir);
+ dir=xstrdup(p);
+ state=START_INFO;
}
FinderJob::prf_res FinderJob::ProcessFile(const char *d,const FileInfo *f)
@@ -248,7 +261,6 @@
void FinderJob::Init()
{
op="find";
- start_dir=0;
init_dir=0;
dir=0;
errors=0;
@@ -260,17 +272,18 @@
show_sl=true;
- depth_first=false; // useful for rm -r
- depth_done=false;
+ contents_first=false; // useful for rm -r
+ contents_done=false;
file_info_need=0;
use_cache=true;
+ validate_args=false;
quiet=false;
maxdepth=-1;
exclude=0;
- state=INIT;
+ state=START_INFO;
}
FinderJob::FinderJob(FileAccess *s)
@@ -283,10 +296,21 @@
void FinderJob::NextDir(const char *d)
{
session->Chdir(init_dir,false); // no verification
- xfree(start_dir);
- start_dir=xstrdup(dir_file(session->GetCwd(),d));
- Down(start_dir);
- Enter(d); /* tell derived that we entered the base path */
+
+#if 0
+ ParsedURL u(d,true);
+ if(u.proto)
+ {
+ session->Close();
+ Reuse(session);
+ FileAccess *newsession=FileAccess::New(&u);
+ newsession->SetPriority(session->GetPriority());
+ session=newsession;
+ d=u.path;
+ }
+#endif
+
+ Down(d);
}
FinderJob::~FinderJob()
@@ -294,9 +318,9 @@
while(stack_ptr>=0)
Up();
xfree(stack);
- xfree(start_dir);
xfree(init_dir);
xfree(exclude);
+ xfree(dir);
Delete(li);
}
@@ -370,6 +394,7 @@
show_sl = !o->usesfd(1);
buf=new IOBufferFDStream(o,IOBuffer::PUT);
NextDir(a->getcurr());
+ ValidateArgs();
}
FinderJob_List::~FinderJob_List()
@@ -389,6 +414,7 @@
// FinderJob_Cmd implementation
// process directory tree
+#undef super
#define super FinderJob
FinderJob::prf_res FinderJob_Cmd::ProcessFile(const char *d,const FileInfo *f)
@@ -397,7 +423,6 @@
#define ISLINK ((f->defined&f->TYPE) && f->filetype==f->SYMLINK)
FileAccess *s=session->Clone();
- s->Chdir(dir_file(start_dir,d),false);
CmdExec *exec=new CmdExec(s);
exec->SetParentFg(this);
@@ -457,7 +482,7 @@
op=args->a0();
use_cache=false;
if(cmd==RM)
- depth_first=true;
+ contents_first=true;
saved_cwd=xgetcwd();
removing_last=false;
NextDir(a->getcurr());
@@ -480,26 +505,6 @@
{
if(cmd==RM)
{
- if(removing_last)
- removing_last=false;
- else
- {
- /* remove the specified directory at the last */
- session->Chdir(init_dir,false); // to leave the directory
- CmdExec *exec=new CmdExec(session->Clone());
- exec->SetParentFg(this);
- exec->FeedCmd("rmdir ");
- if(quiet)
- exec->FeedCmd("-f ");
- exec->FeedCmd("-- ");
- exec->FeedQuoted(start_dir);
- exec->FeedCmd("\n");
- AddWaiting(exec);
- removing_last=true;
- state=WAIT; // it will wait for termination, try to process
- // next file and call Finish() again
- return;
- }
}
char *d=args->getnext();
if(!d)
Index: src/FindJob.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/FindJob.h,v
retrieving revision 1.9
diff -u -r1.9 FindJob.h
--- src/FindJob.h 2001/11/09 13:36:27 1.9
+++ src/FindJob.h 2001/12/10 19:48:27
@@ -26,12 +26,13 @@
#include "Job.h"
#include "buffer.h"
#include "ArgV.h"
+#include "GetFileInfo.h"
class FinderJob : public SessionJob
{
- const char *dir;
+ char *dir;
int errors;
- ListInfo *li;
+ GetFileInfo *li;
class place
{
@@ -55,17 +56,23 @@
virtual void Enter(const char *d) { }
virtual void Exit() { }
- bool depth_done;
+ bool contents_done;
unsigned file_info_need;
+ /* In certain circumstances, we can skip a LIST altogether and just
+ * pass argument names on: we don't need anything other than the name
+ * (no other file_info_needs) and we're not recursing (which would imply
+ * needing the type.) This means arguments that don't actually exist
+ * get passed on; if this is inappropriate (ie for a simple Find),
+ * call ValidateArgs(). */
+ bool validate_args;
char *exclude;
protected:
- enum state_t { INIT, CD, INFO, LOOP, WAIT, DONE };
+ enum state_t { START_INFO, INFO, LOOP, WAIT, DONE };
state_t state;
const char *op;
- char *start_dir;
char *init_dir;
enum prf_res { PRF_FATAL, PRF_ERR, PRF_OK, PRF_WAIT, PRF_LATER };
@@ -75,7 +82,7 @@
bool show_sl;
- bool depth_first;
+ bool contents_first;
bool use_cache;
bool quiet;
int maxdepth;
@@ -83,6 +90,7 @@
void NextDir(const char *d);
const char *GetCur() const { return dir; }
void Need(unsigned need) { file_info_need=need; }
+ void ValidateArgs() { validate_args=true; }
public:
int Do();
@@ -91,10 +99,10 @@
void Init();
FinderJob(FileAccess *s);
- ~FinderJob();
+ virtual ~FinderJob();
void ShowRunStatus(StatusLine *sl);
- void PrintStatus(int v);
+ virtual void PrintStatus(int v);
void BeQuiet() { quiet=true; }
void SetExclude(const char *excl) { xfree(exclude); exclude = xstrdup(excl); }
Index: src/FindJobDu.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/FindJobDu.cc,v
retrieving revision 1.3
diff -u -r1.3 FindJobDu.cc
--- src/FindJobDu.cc 2001/11/15 12:52:53 1.3
+++ src/FindJobDu.cc 2001/12/10 19:48:27
@@ -124,10 +124,11 @@
size_stack[stack_ptr].size += add;
tot_size += add;
- if(all_files) {
+ if(all_files || stack_ptr == -1) {
/* this is <, where Pop() is <=, since the file counts in depth */
if(max_print_depth == -1 || stack_ptr < max_print_depth)
- print_size(fi->size, dir_file(size_stack[stack_ptr].dir,fi->name));
+ print_size(fi->size,
+ dir_file(stack_ptr == -1? "":size_stack[stack_ptr].dir,fi->name));
}
return PRF_OK;
Index: src/GetFileInfo.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/GetFileInfo.cc,v
retrieving revision 1.7
diff -u -r1.7 GetFileInfo.cc
--- src/GetFileInfo.cc 2001/12/10 10:13:45 1.7
+++ src/GetFileInfo.cc 2001/12/10 19:48:27
@@ -14,9 +14,9 @@
* To find out if a path is a directory, attempt to chdir into it. If it
* succeeds it's a path, otherwise it's a directory (or there was an error).
* Do this by setting the verify argument to Chdir() to true.
- *
+ *
* If the cache knows the file type of _dir, avoid changing directories if
- * possible, so cached listings don't touch the connection at all.
+ * possible, so cached listings don't touch the connection at all.
*
* We still need to Chdir() if we're operating out of cache (that's how you
* look up cache entries). However, we don't really want to change the
@@ -28,30 +28,37 @@
* fairly contrived situations (ie. "cls dir/", then change a directory in
* dir/ to a file). It's not possible to fix completely, and a partial fix
* would cause other problems, so it's not worth bothering with.
- */
+ */
+
GetFileInfo::GetFileInfo(FileAccess *a, const char *_dir, bool _showdir)
: ListInfo(a,0)
{
- dir=xstrdup(_dir? _dir:"");
+ dir=xstrdup(_dir? _dir:"", 16);
+
showdir=_showdir;
state=INITIAL;
tried_dir=tried_file=false;
result=0;
- realdir=0;
+ path_to_prefix=0;
+ verify_fn=0;
li=0;
from_cache=0;
saved_error_text=0;
+ was_directory=0;
+ prepend_path=true;
origdir=xstrdup(session->GetCwd());
}
GetFileInfo::~GetFileInfo()
{
+ session->Close();
Delete(li);
xfree(saved_error_text);
xfree(dir);
- xfree(realdir);
+ xfree(path_to_prefix);
xfree(origdir);
+ xfree(verify_fn);
}
int GetFileInfo::Do()
@@ -67,6 +74,8 @@
case INITIAL:
state=CHANGE_DIR;
+ if(showdir)
+ tried_dir=true;
/* if we're not showing directories, try to skip tests we don't need */
if(use_cache && !showdir) switch(LsCache::IsDirectory(session,dir))
{
@@ -79,10 +88,11 @@
from_cache = true;
break;
}
-
+
assert(!tried_dir || !tried_file); /* always do at least one */
case CHANGE_DIR:
+ {
if(tried_dir && tried_file) {
/* We tried both; no luck. Fail. */
SetError(saved_error_text);
@@ -91,33 +101,51 @@
return MOVED;
}
+ const char *cd_path;
if(!tried_dir)
{
/* First, try to treat the path as a directory. */
tried_dir=true;
- realdir = xstrdup(dir);
+ cd_path = dir;
+ path_to_prefix=xstrdup(dir);
+ was_directory=true;
}
else if(!tried_file)
{
tried_file=true;
- xfree(realdir);
- realdir = xstrdup(dir);
- /* If the path ends with a slash, and we're showing directories, remove it. */
- if(*realdir && realdir[strlen(realdir)-1] == '/')
- realdir[strlen(realdir)-1] = 0;
-
- char *slash = strrchr(realdir, '/');
- if(!slash)
- realdir=xstrdup(""); /* file with no path */
- else
- *slash=0;
+ /* CD into the full path (without validation), and grab the
+ * path's basename. */
+ session->Chdir(dir, false);
+ verify_fn = xstrdup(basename_ptr(session->GetCwd()));
+
+ /* Now go to the parent directory to list the directory we now
+ * have a name for: */
+ cd_path = "..";
+
+ xfree(path_to_prefix);
+ path_to_prefix=dirname_alloc(dir);
+
+ /* Special case: looking up "/". Make a phony "/" entry. */
+ if(showdir && !strcmp(verify_fn, "/"))
+ {
+ FileInfo *fi = new FileInfo(verify_fn);
+ fi->SetType(fi->DIRECTORY);
+
+ result = new FileSet;
+ result->Add(fi);
+ state=DONE;
+ return MOVED;
+ }
+
+ was_directory=false;
}
/* See top comments for logic here: */
- session->Chdir(realdir, !from_cache);
+ session->Chdir(cd_path, !from_cache);
state=CHANGING_DIR;
m=MOVED;
+ }
case CHANGING_DIR:
res=session->Done();
@@ -162,15 +190,13 @@
result=li->GetResult();
Delete(li); li=0;
- /* If this was a listing of the dirname: */
- if(strcmp(realdir, dir)) {
- char *filename = xstrdup(basename_ptr(dir));
- if(filename[strlen(filename)-1] == '/')
- filename[strlen(filename)-1] = 0;
+ /* If this was a listing of the basename: */
+ if(!was_directory) {
+ if(verify_fn[strlen(verify_fn)-1] == '/')
+ verify_fn[strlen(verify_fn)-1] = 0;
/* Find the file with our filename: */
- FileInfo *file = result->FindByName(filename);
- xfree(filename);
+ const FileInfo *file = result->FindByName(verify_fn);
if(!file) {
/* The file doesn't exist; fail. */
@@ -193,17 +219,32 @@
}
FileSet *newresult=new FileSet();
- newresult->Add(new FileInfo(*file));
+ FileInfo *copy = new FileInfo(*file);
+
+ newresult->Add(copy);
delete result;
result=newresult;
}
- result->PrependPath(realdir);
-
done:
case DONE:
if(!done)
{
+ if(showdir && result->get_fnum())
+ {
+ FileInfo *f = (*result)[0];
+ /* Make sure the filename is what was requested (ie ".."). */
+ char *fn = basename_ptr(dir);
+ f->SetName(*fn? fn:".");
+
+ /* If we're in show_dir mode, was_directory will always be false;
+ * set it to whether the single file is actually a directory or not. */
+ if(f->defined&f->TYPE)
+ was_directory = (f->filetype == f->DIRECTORY);
+ }
+
+ if(prepend_path)
+ result->PrependPath(path_to_prefix);
done=true;
session->Chdir(origdir, false);
}
Index: src/GetFileInfo.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/GetFileInfo.h,v
retrieving revision 1.3
diff -u -r1.3 GetFileInfo.h
--- src/GetFileInfo.h 2001/12/10 10:13:46 1.3
+++ src/GetFileInfo.h 2001/12/10 19:48:27
@@ -34,10 +34,15 @@
char *dir;
/* directory we've actually listed: */
- char *realdir;
+ char *path_to_prefix;
+ /* directory we started in: */
char *origdir;
+ /* In showdir mode, we make sure the path actually exists; this is
+ * the filename to look for. */
+ char *verify_fn;
+
bool showdir;
enum state_t { INITIAL, CHANGE_DIR, CHANGING_DIR, GETTING_LIST, DONE } state;
@@ -47,6 +52,10 @@
bool tried_file;
/* whether we found out the file type from cache */
bool from_cache;
+ /* whether the given path was a file or directory. */
+ bool was_directory;
+ /* if true, prepend the appropriate relative path to the result */
+ bool prepend_path;
char *saved_error_text;
@@ -56,6 +65,8 @@
int Do();
const char *Status();
+ bool WasDirectory() const { return was_directory; }
+ void DontPrependPath() { prepend_path=false; }
};
#endif /* GETFILEINFO_H */
Index: src/TreatFileJob.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/TreatFileJob.cc,v
retrieving revision 1.3
diff -u -r1.3 TreatFileJob.cc
--- src/TreatFileJob.cc 2000/01/22 16:27:30 1.3
+++ src/TreatFileJob.cc 2001/12/10 19:48:29
@@ -24,59 +24,74 @@
#include "TreatFileJob.h"
#include "StatusLine.h"
#include "ArgV.h"
+#include "misc.h"
-TreatFileJob::TreatFileJob(FileAccess *s,ArgV *a) : SessionJob(s)
+TreatFileJob::TreatFileJob(FileAccess *s,ArgV *a) : FinderJob(s)
{
- done=false;
quiet=false;
failed=file_count=0;
- url_session=0;
+ Need(FileInfo::NAME);
+ curr=0;
+ first=0;
+ set_maxdepth(0);
+
args=a;
+ op=args->a0();
+ Begin(a->getcurr());
+}
- curr=first=0;
- args->rewind();
+/* process a new directory */
+void TreatFileJob::Begin(const char *d)
+{
+ NextDir(d);
+}
- op=args->a0();
+void TreatFileJob::Finish()
+{
+ /* next? */
+ char *d=args->getnext();
+ if(d) {
+ /* we have another argument */
+ Begin(d);
+ return;
+ }
}
TreatFileJob::~TreatFileJob()
{
delete args;
- Reuse();
+ delete first;
}
-int TreatFileJob::Do()
+TreatFileJob::prf_res TreatFileJob::ProcessFile(const char *d,const FileInfo *fi)
{
- if(Done())
- return STALL;
- if(!curr)
+ curr=fi;
+ if(session->IsClosed())
{
- curr=args->getnext();
- if(!curr)
- {
- done=true;
- return MOVED;
- }
if(!first)
- first=curr;
+ first=new FileInfo(*fi);
+
+ TreatCurrent(d,fi);
+ return PRF_LATER;
}
- if(Session()->IsClosed())
- TreatCurrent();
- int res=Session()->Done();
+ int res=session->Done();
if(res==FA::IN_PROGRESS)
- return STALL;
+ return PRF_LATER;
+
+ curr=0;
+ file_count++;
+
if(res<0)
{
failed++;
if(!quiet)
- fprintf(stderr,"%s: %s\n",op,Session()->StrError(res));
+ fprintf(stderr,"%s: %s\n",op,session->StrError(res));
}
- file_count++;
- Session()->Close();
- Reuse();
- curr=0;
- return MOVED;
+ CurrentFinished(d,fi);
+
+ session->Close();
+ return res<0? PRF_ERR:PRF_OK;
}
void TreatFileJob::PrintStatus(int v)
@@ -84,16 +99,12 @@
SessionJob::PrintStatus(v);
if(Done() || !curr)
return;
- printf("\t`%s' [%s]\n",curr,Session()->CurrentStatus());
+ printf("\t`%s' [%s]\n",curr->name,session->CurrentStatus());
}
void TreatFileJob::ShowRunStatus(StatusLine *s)
{
if(curr && !Done())
- s->Show("%s `%s' [%s]",op,curr,Session()->CurrentStatus());
+ s->Show("%s `%s' [%s]",op,curr->name,session->CurrentStatus());
}
-void TreatFileJob::AddFile(const char *f)
-{
- args->Append(f);
-}
Index: src/TreatFileJob.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/TreatFileJob.h,v
retrieving revision 1.2
diff -u -r1.2 TreatFileJob.h
--- src/TreatFileJob.h 2000/01/22 16:27:30 1.2
+++ src/TreatFileJob.h 2001/12/10 19:48:29
@@ -22,48 +22,34 @@
#define TREATFILEJOB_H
#include "Job.h"
+#include "FindJob.h"
class StatusLine;
class ArgV;
-class TreatFileJob : public SessionJob
+class TreatFileJob : public FinderJob
{
protected:
ArgV *args;
- const char *curr;
- const char *first;
- const char *op;
+ const FileInfo *curr;
+ FileInfo *first;
int failed,file_count;
- bool quiet;
- bool done;
- virtual void TreatCurrent() = 0;
+ virtual void TreatCurrent(const char *d,const FileInfo *fi) = 0;
+ virtual void CurrentFinished(const char *d,const FileInfo *fi) { }
- FA *url_session;
- FA *Session() { return url_session?url_session:session; }
- void Reuse()
- {
- if(url_session)
- {
- SessionPool::Reuse(url_session);
- url_session=0;
- }
- }
+ void Begin(const char *d);
+
+ /* virtuals */
+ void Finish();
+ prf_res ProcessFile(const char *d,const FileInfo *fi);
public:
- int Do();
- int Done() { return done; }
- int ExitCode() { return failed; }
-
void PrintStatus(int);
void ShowRunStatus(StatusLine *);
TreatFileJob(FileAccess *session,ArgV *a);
- ~TreatFileJob();
-
- void BeQuiet() { quiet=true; }
-
- void AddFile(const char *f);
+ virtual ~TreatFileJob();
};
#endif // TREATFILEJOB_H
Index: src/commands.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/commands.cc,v
retrieving revision 1.161
diff -u -r1.161 commands.cc
--- src/commands.cc 2001/11/30 09:49:44 1.161
+++ src/commands.cc 2001/12/10 19:48:32
@@ -144,7 +144,15 @@
"is stored as `-'. You can do `cd -' to change the directory back.\n"
"The previous directory for each site is also stored on disk, so you can\n"
"do `open site; cd -' even after lftp restart.\n")},
- {"chmod", cmd_chmod, N_("chmod mode file..."), 0},
+ {"chmod", cmd_chmod, N_("chmod [OPTS] mode file..."),
+ N_("Usage: chmod [OPTION]... MODE[,MODE]... FILE...\n"
+ "or: chmod [OPTION]... OCTAL-MODE FILE...\n"
+ "Change the mode of each FILE to MODE.\n"
+ "\n"
+ " -c, --changes - like verbose but report only when a change is
+made\n"
+ " -f, --quiet - suppress most error messages\n"
+ " -v, --verbose - output a diagnostic for every file processed\n"
+ " -R, --recursive - change files and directories recursively\n")},
{"close", cmd_close, "close [-a]",
N_("Close idle connections. By default only with current server.\n"
" -a close idle connections with all servers\n")},
@@ -1618,29 +1626,18 @@
return 0;
}
}
- args->back();
- char *curr=args->getnext();
- if(curr==0)
- goto print_usage;
- if(recursive)
- {
- FinderJob *j=new FinderJob_Cmd(Clone(),args,FinderJob_Cmd::RM);
- args=0;
- if(silent)
- j->BeQuiet();
- return j;
- }
+ if(args->getcurr()==0)
+ goto print_usage;
rmJob *j=(rmdir?
- new rmdirJob(Clone(),new ArgV(args->a0())):
- new rmJob(Clone(),new ArgV(args->a0())));
+ new rmdirJob(Clone(),args):
+ new rmJob(Clone(),args));
- while(curr)
- {
- j->AddFile(curr);
- curr=args->getnext();
- }
+ if(recursive)
+ j->Recurse();
+
+ args=0;
if(silent)
j->BeQuiet();
@@ -2460,12 +2457,12 @@
{"maxdepth",required_argument,0,'d'},
{0,0,0,0}
};
- int opt;
+ int opt, longopt;
int maxdepth = -1;
const char *op=args->a0();
args->rewind();
- while((opt=args->getopt_long("+d:",find_options,0))!=EOF)
+ while((opt=args->getopt_long("+d:",find_options,&longopt))!=EOF)
{
switch(opt)
{
@@ -2484,7 +2481,7 @@
}
if(!args->getcurr())
- args->Append("");
+ args->Append(".");
FinderJob_List *j=new class FinderJob_List(Clone(),args,
output?output:new FDStream(1,"<stdout>"));
j->set_maxdepth(maxdepth);
@@ -2683,19 +2680,79 @@
CMD(chmod)
{
- if(args->count()<3)
+ ChmodJob::verbosity verbose = ChmodJob::V_NONE;
+ bool recurse = false, quiet = false;
+
+ static struct option chmod_options[]=
{
- eprintf(_("Usage: %s mode file...\n"),args->a0());
- return 0;
- }
- int m;
- if(sscanf(args->getarg(1),"%o",&m)!=1)
+ {"verbose",no_argument,0,'v'},
+ {"changes",no_argument,0,'c'},
+ {"recursive",no_argument,0,'R'},
+ {"silent",no_argument,0,'f'},
+ {"quiet",no_argument,0,'f'},
+ {0,0,0,0}
+ };
+ int opt;
+ int modeind = 0;
+
+ while((opt=args->getopt_long("vcRfrwxXstugoa,+-=",chmod_options,0))!=EOF)
{
- eprintf(_("%s: %s - not an octal number\n"),args->a0(),args->getarg(1));
+ switch(opt)
+ {
+ case 'r': case 'w': case 'x':
+ case 'X': case 's': case 't':
+ case 'u': case 'g': case 'o':
+ case 'a':
+ case ',':
+ case '+': case '=':
+ modeind = optind?optind-1:1;
+ break; /* mode string that begins with - */
+
+ case 'v':
+ verbose=ChmodJob::V_ALL;
+ break;
+ case 'c':
+ verbose=ChmodJob::V_CHANGES;
+ break;
+ case 'R':
+ recurse = true;
+ break;
+ case 'f':
+ quiet = true;
+ break;
+
+ case '?':
+ usage:
+ eprintf(_("Usage: %s [OPTS] mode file...\n"),args->a0());
+ return 0;
+ }
+ }
+
+ if(modeind == 0)
+ modeind = args->getindex();
+
+ char *arg = alloca_strdup(args->getarg(modeind));
+ if(!arg)
+ goto usage;
+ args->delarg(modeind);
+
+ if(!args->getcurr())
+ goto usage;
+
+ mode_change *m = mode_compile(arg, MODE_MASK_ALL);
+ if(m == MODE_INVALID)
+ {
+ eprintf(_("invalid mode string: %s\n"), arg);
return 0;
}
- args->delarg(1);
- Job *j=new ChmodJob(Clone(),m,args);
+
+ ChmodJob *j=new ChmodJob(Clone(),args);
+ j->SetVerbosity(verbose);
+ j->SetMode(m);
+ if(quiet)
+ j->BeQuiet(); /* does not affect messages from Verbosity */
+ if(recurse)
+ j->Recurse();
args=0;
return j;
}
Index: src/misc.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/misc.cc,v
retrieving revision 1.44
diff -u -r1.44 misc.cc
--- src/misc.cc 2001/11/26 14:20:02 1.44
+++ src/misc.cc 2001/12/10 19:48:37
@@ -60,6 +60,8 @@
return dir;
if(file[0]=='/')
return file;
+ if(file[0]=='~' && (!file[1] || file[1] == '/'))
+ return file;
static char *buf=0;
static int buf_size=0;
@@ -748,3 +750,28 @@
va_end(va);
return ret;
}
+
+/* /file/name -> /file
+ * /file -> /
+ * file/name -> "file"
+ * file/name/ -> "file"
+ * file -> ""
+ * note: last differs from dirname (wwould return ".")
+ *
+ */
+char *dirname_alloc(const char *fn)
+{
+ char *ret=xstrdup(fn);
+ /* remove trailing slash */
+ if(ret[strlen(ret)-1] == '/')
+ ret[strlen(ret)-1] = 0;
+
+ char *slash = strrchr(ret, '/');
+ if(!slash)
+ ret[0]=0; /* file with no path */
+ else if(slash == fn)
+ ret[1]=0; /* the slash is the first character */
+ else
+ *slash=0;
+ return ret;
+}
Index: src/misc.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/misc.h,v
retrieving revision 1.28
diff -u -r1.28 misc.h
--- src/misc.h 2001/11/26 14:20:02 1.28
+++ src/misc.h 2001/12/10 19:48:37
@@ -109,4 +109,6 @@
char *xvasprintf(const char *format, va_list ap);
char *xasprintf(const char *format, ...);
+char *dirname_alloc(const char *fn);
+
#endif // MISC_H
Index: src/rmJob.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/rmJob.cc,v
retrieving revision 1.6
diff -u -r1.6 rmJob.cc
--- src/rmJob.cc 2000/08/25 23:19:28 1.6
+++ src/rmJob.cc 2001/12/10 19:48:37
@@ -26,18 +26,27 @@
#include "rmJob.h"
#include "plural.h"
#include "url.h"
+#include "misc.h"
rmJob::rmJob(FileAccess *s,ArgV *a) : TreatFileJob(s,a)
{
mode=FA::REMOVE;
+ recurse=false;
+ contents_first=true;
}
+void rmJob::Recurse()
+{
+ set_maxdepth(-1);
+ Need(FileInfo::TYPE);
+}
+
void rmJob::SayFinal()
{
if(failed==file_count)
return;
if(file_count==1)
- printf(_("%s ok, `%s' removed\n"),op,first);
+ printf(_("%s ok, `%s' removed\n"),op,first->name);
else if(failed)
{
if(mode==FA::REMOVE_DIR)
@@ -58,13 +67,12 @@
}
}
-void rmJob::TreatCurrent()
+void rmJob::TreatCurrent(const char *d, const FileInfo *fi)
{
- ParsedURL u(curr,true);
- if(u.proto)
- url_session=FA::New(&u);
- if(url_session)
- url_session->Open(u.path,mode);
+ /* If we're recursing and this is a directory, rmdir it. (If we're
+ * not recursing, just send an rm and let it fail.) */
+ if(recurse && (fi->defined&fi->TYPE) && (fi->filetype==fi->DIRECTORY))
+ session->Open(fi->name,FA::REMOVE_DIR);
else
- session->Open(curr,mode);
+ session->Open(fi->name,mode);
}
Index: src/rmJob.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/rmJob.h,v
retrieving revision 1.3
diff -u -r1.3 rmJob.h
--- src/rmJob.h 1999/05/26 14:04:10 1.3
+++ src/rmJob.h 2001/12/10 19:48:37
@@ -25,13 +25,16 @@
class rmJob : public TreatFileJob
{
- void TreatCurrent();
+ void TreatCurrent(const char *, const FileInfo *);
protected:
FA::open_mode mode;
+ bool recurse;
+
public:
- void SayFinal();
+ void SayFinal();
+ void Recurse(); // rm -r
rmJob(FileAccess *session,ArgV *a);
};
/* modechange.c -- file mode manipulation
Copyright (C) 1989, 1990, 1997, 1998, 1999, 2001 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/* Written by David MacKenzie <[EMAIL PROTECTED]> */
/* The ASCII mode string is compiled into a linked list of `struct
modechange', which can then be applied to each file to be changed.
We do this instead of re-parsing the ASCII string for each file
because the compiled form requires less computation to use; when
changing the mode of many files, this probably results in a
performance gain. */
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include "modechange.h"
#include <sys/stat.h>
#include "xstrtol.h"
#if STDC_HEADERS
# include <stdlib.h>
#else
char *malloc ();
#endif
#ifndef NULL
# define NULL 0
#endif
#if STAT_MACROS_BROKEN
# undef S_ISDIR
#endif
#if !defined(S_ISDIR) && defined(S_IFDIR)
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
/* The traditional octal values corresponding to each mode bit. */
#define SUID 04000
#define SGID 02000
#define SVTX 01000
#define RUSR 00400
#define WUSR 00200
#define XUSR 00100
#define RGRP 00040
#define WGRP 00020
#define XGRP 00010
#define ROTH 00004
#define WOTH 00002
#define XOTH 00001
#define ALLM 07777 /* all octal mode bits */
#ifndef S_ISUID
# define S_ISUID SUID
#endif
#ifndef S_ISGID
# define S_ISGID SGID
#endif
#ifndef S_ISVTX
# define S_ISVTX SVTX
#endif
#ifndef S_IRUSR
# define S_IRUSR RUSR
#endif
#ifndef S_IWUSR
# define S_IWUSR WUSR
#endif
#ifndef S_IXUSR
# define S_IXUSR XUSR
#endif
#ifndef S_IRGRP
# define S_IRGRP RGRP
#endif
#ifndef S_IWGRP
# define S_IWGRP WGRP
#endif
#ifndef S_IXGRP
# define S_IXGRP XGRP
#endif
#ifndef S_IROTH
# define S_IROTH ROTH
#endif
#ifndef S_IWOTH
# define S_IWOTH WOTH
#endif
#ifndef S_IXOTH
# define S_IXOTH XOTH
#endif
#ifndef S_IRWXU
# define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
#endif
#ifndef S_IRWXG
# define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
#endif
#ifndef S_IRWXO
# define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
#endif
/* All the mode bits that can be affected by chmod. */
#define CHMOD_MODE_BITS \
(S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
/* Return newly allocated memory to hold one element of type TYPE. */
#define talloc(type) ((type *) malloc (sizeof (type)))
/* Create a mode_change entry with the specified `=ddd'-style
mode change operation, where NEW_MODE is `ddd'. Return the
new entry, or NULL upon failure. */
static struct mode_change *
make_node_op_equals (mode_t new_mode)
{
struct mode_change *p;
p = talloc (struct mode_change);
if (p == NULL)
return p;
p->next = NULL;
p->op = '=';
p->flags = 0;
p->value = new_mode;
p->affected = CHMOD_MODE_BITS; /* Affect all permissions. */
return p;
}
/* Append entry E to the end of the link list with the specified
HEAD and TAIL. */
static void
mode_append_entry (struct mode_change **head,
struct mode_change **tail,
struct mode_change *e)
{
if (*head == NULL)
*head = *tail = e;
else
{
(*tail)->next = e;
*tail = e;
}
}
/* Return a linked list of file mode change operations created from
MODE_STRING, an ASCII string that contains either an octal number
specifying an absolute mode, or symbolic mode change operations with
the form:
[ugoa...][[+-=][rwxXstugo...]...][,...]
MASKED_OPS is a bitmask indicating which symbolic mode operators (=+-)
should not affect bits set in the umask when no users are given.
Operators not selected in MASKED_OPS ignore the umask.
Return MODE_INVALID if `mode_string' does not contain a valid
representation of file mode change operations;
return MODE_MEMORY_EXHAUSTED if there is insufficient memory. */
struct mode_change *
mode_compile (const char *mode_string, unsigned int masked_ops)
{
struct mode_change *head; /* First element of the linked list. */
struct mode_change *tail; /* An element of the linked list. */
unsigned long octal_value; /* The mode value, if octal. */
mode_t umask_value; /* The umask value (surprise). */
head = NULL;
#ifdef lint
tail = NULL;
#endif
if (xstrtoul (mode_string, NULL, 8, &octal_value, "") == LONGINT_OK)
{
struct mode_change *p;
mode_t mode;
if (octal_value != (octal_value & ALLM))
return MODE_INVALID;
/* Help the compiler optimize the usual case where mode_t uses
the traditional octal representation. */
mode = ((S_ISUID == SUID && S_ISGID == SGID && S_ISVTX == SVTX
&& S_IRUSR == RUSR && S_IWUSR == WUSR && S_IXUSR == XUSR
&& S_IRGRP == RGRP && S_IWGRP == WGRP && S_IXGRP == XGRP
&& S_IROTH == ROTH && S_IWOTH == WOTH && S_IXOTH == XOTH)
? octal_value
: ((octal_value & SUID ? S_ISUID : 0)
| (octal_value & SGID ? S_ISGID : 0)
| (octal_value & SVTX ? S_ISVTX : 0)
| (octal_value & RUSR ? S_IRUSR : 0)
| (octal_value & WUSR ? S_IWUSR : 0)
| (octal_value & XUSR ? S_IXUSR : 0)
| (octal_value & RGRP ? S_IRGRP : 0)
| (octal_value & WGRP ? S_IWGRP : 0)
| (octal_value & XGRP ? S_IXGRP : 0)
| (octal_value & ROTH ? S_IROTH : 0)
| (octal_value & WOTH ? S_IWOTH : 0)
| (octal_value & XOTH ? S_IXOTH : 0)));
p = make_node_op_equals (mode);
if (p == NULL)
return MODE_MEMORY_EXHAUSTED;
mode_append_entry (&head, &tail, p);
return head;
}
umask_value = umask (0);
umask (umask_value); /* Restore the old value. */
--mode_string;
/* One loop iteration for each "ugoa...=+-rwxXstugo...[=+-rwxXstugo...]". */
do
{
/* Which bits in the mode are operated on. */
mode_t affected_bits = 0;
/* `affected_bits' modified by umask. */
mode_t affected_masked;
/* Operators to actually use umask on. */
unsigned ops_to_mask = 0;
int who_specified_p;
affected_bits = 0;
ops_to_mask = 0;
/* Turn on all the bits in `affected_bits' for each group given. */
for (++mode_string;; ++mode_string)
switch (*mode_string)
{
case 'u':
affected_bits |= S_ISUID | S_IRWXU;
break;
case 'g':
affected_bits |= S_ISGID | S_IRWXG;
break;
case 'o':
affected_bits |= S_ISVTX | S_IRWXO;
break;
case 'a':
affected_bits |= CHMOD_MODE_BITS;
break;
default:
goto no_more_affected;
}
no_more_affected:
/* If none specified, affect all bits, except perhaps those
set in the umask. */
if (affected_bits)
who_specified_p = 1;
else
{
who_specified_p = 0;
affected_bits = CHMOD_MODE_BITS;
ops_to_mask = masked_ops;
}
while (*mode_string == '=' || *mode_string == '+' || *mode_string == '-')
{
struct mode_change *change = talloc (struct mode_change);
if (change == NULL)
{
mode_free (head);
return MODE_MEMORY_EXHAUSTED;
}
change->next = NULL;
change->op = *mode_string; /* One of "=+-". */
affected_masked = affected_bits;
/* Per the Single Unix Spec, if `who' is not specified and the
`=' operator is used, then clear all the bits first. */
if (!who_specified_p &&
ops_to_mask & (*mode_string == '=' ? MODE_MASK_EQUALS : 0))
{
struct mode_change *p = make_node_op_equals (0);
if (p == NULL)
return MODE_MEMORY_EXHAUSTED;
mode_append_entry (&head, &tail, p);
}
if (ops_to_mask & (*mode_string == '=' ? MODE_MASK_EQUALS
: *mode_string == '+' ? MODE_MASK_PLUS
: MODE_MASK_MINUS))
affected_masked &= ~umask_value;
change->affected = affected_masked;
change->value = 0;
change->flags = 0;
/* Add the element to the tail of the list, so the operations
are performed in the correct order. */
mode_append_entry (&head, &tail, change);
/* Set `value' according to the bits set in `affected_masked'. */
for (++mode_string;; ++mode_string)
switch (*mode_string)
{
case 'r':
change->value |= ((S_IRUSR | S_IRGRP | S_IROTH)
& affected_masked);
break;
case 'w':
change->value |= ((S_IWUSR | S_IWGRP | S_IWOTH)
& affected_masked);
break;
case 'X':
change->flags |= MODE_X_IF_ANY_X;
/* Fall through. */
case 'x':
change->value |= ((S_IXUSR | S_IXGRP | S_IXOTH)
& affected_masked);
break;
case 's':
/* Set the setuid/gid bits if `u' or `g' is selected. */
change->value |= (S_ISUID | S_ISGID) & affected_masked;
break;
case 't':
/* Set the "save text image" bit if `o' is selected. */
change->value |= S_ISVTX & affected_masked;
break;
case 'u':
/* Set the affected bits to the value of the `u' bits
on the same file. */
if (change->value)
goto invalid;
change->value = S_IRWXU;
change->flags |= MODE_COPY_EXISTING;
break;
case 'g':
/* Set the affected bits to the value of the `g' bits
on the same file. */
if (change->value)
goto invalid;
change->value = S_IRWXG;
change->flags |= MODE_COPY_EXISTING;
break;
case 'o':
/* Set the affected bits to the value of the `o' bits
on the same file. */
if (change->value)
goto invalid;
change->value = S_IRWXO;
change->flags |= MODE_COPY_EXISTING;
break;
default:
goto no_more_values;
}
no_more_values:;
}
} while (*mode_string == ',');
if (*mode_string == 0)
return head;
invalid:
mode_free (head);
return MODE_INVALID;
}
/* Return a file mode change operation that sets permissions to match those
of REF_FILE. Return MODE_BAD_REFERENCE if REF_FILE can't be accessed. */
struct mode_change *
mode_create_from_ref (const char *ref_file)
{
struct mode_change *change; /* the only change element */
struct stat ref_stats;
if (stat (ref_file, &ref_stats))
return MODE_BAD_REFERENCE;
change = talloc (struct mode_change);
if (change == NULL)
return MODE_MEMORY_EXHAUSTED;
change->op = '=';
change->flags = 0;
change->affected = CHMOD_MODE_BITS;
change->value = ref_stats.st_mode;
change->next = NULL;
return change;
}
/* Return file mode OLDMODE, adjusted as indicated by the list of change
operations CHANGES. If OLDMODE is a directory, the type `X'
change affects it even if no execute bits were set in OLDMODE.
The returned value has the S_IFMT bits cleared. */
mode_t
mode_adjust (mode_t oldmode, const struct mode_change *changes)
{
mode_t newmode; /* The adjusted mode and one operand. */
mode_t value; /* The other operand. */
newmode = oldmode & CHMOD_MODE_BITS;
for (; changes; changes = changes->next)
{
if (changes->flags & MODE_COPY_EXISTING)
{
/* Isolate in `value' the bits in `newmode' to copy, given in
the mask `changes->value'. */
value = newmode & changes->value;
if (changes->value & S_IRWXU)
/* Copy `u' permissions onto `g' and `o'. */
value |= ((value & S_IRUSR ? S_IRGRP | S_IROTH : 0)
| (value & S_IWUSR ? S_IWGRP | S_IROTH : 0)
| (value & S_IXUSR ? S_IXGRP | S_IXOTH : 0));
else if (changes->value & S_IRWXG)
/* Copy `g' permissions onto `u' and `o'. */
value |= ((value & S_IRGRP ? S_IRUSR | S_IROTH : 0)
| (value & S_IWGRP ? S_IWUSR | S_IROTH : 0)
| (value & S_IXGRP ? S_IXUSR | S_IXOTH : 0));
else
/* Copy `o' permissions onto `u' and `g'. */
value |= ((value & S_IROTH ? S_IRUSR | S_IRGRP : 0)
| (value & S_IWOTH ? S_IWUSR | S_IRGRP : 0)
| (value & S_IXOTH ? S_IXUSR | S_IXGRP : 0));
/* In order to change only `u', `g', or `o' permissions,
or some combination thereof, clear unselected bits.
This cannot be done in mode_compile because the value
to which the `changes->affected' mask is applied depends
on the old mode of each file. */
value &= changes->affected;
}
else
{
value = changes->value;
/* If `X', do not affect the execute bits if the file is not a
directory and no execute bits are already set. */
if ((changes->flags & MODE_X_IF_ANY_X)
&& !S_ISDIR (oldmode)
&& (newmode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
/* Clear the execute bits. */
value &= ~ (S_IXUSR | S_IXGRP | S_IXOTH);
}
switch (changes->op)
{
case '=':
/* Preserve the previous values in `newmode' of bits that are
not affected by this change operation. */
newmode = (newmode & ~changes->affected) | value;
break;
case '+':
newmode |= value;
break;
case '-':
newmode &= ~value;
break;
}
}
return newmode;
}
/* Free the memory used by the list of file mode change operations
CHANGES. */
void
mode_free (register struct mode_change *changes)
{
register struct mode_change *next;
while (changes)
{
next = changes->next;
free (changes);
changes = next;
}
}
/* modechange.h -- definitions for file mode manipulation
Copyright (C) 1989, 1990, 1997 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/* Masks for the `flags' field in a `struct mode_change'. */
#if ! defined MODECHANGE_H_
# define MODECHANGE_H_
# if HAVE_CONFIG_H
# include <config.h>
# endif
# include <sys/types.h>
/* Affect the execute bits only if at least one execute bit is set already,
or if the file is a directory. */
# define MODE_X_IF_ANY_X 01
/* If set, copy some existing permissions for u, g, or o onto the other two.
Which of u, g, or o is copied is determined by which bits are set in the
`value' field. */
# define MODE_COPY_EXISTING 02
struct mode_change
{
char op; /* One of "=+-". */
char flags; /* Special operations. */
mode_t affected; /* Set for u/g/o/s/s/t, if to be affected. */
mode_t value; /* Bits to add/remove. */
struct mode_change *next; /* Link to next change in list. */
};
/* Masks for mode_compile argument. */
# define MODE_MASK_EQUALS 1
# define MODE_MASK_PLUS 2
# define MODE_MASK_MINUS 4
# define MODE_MASK_ALL (MODE_MASK_EQUALS | MODE_MASK_PLUS | MODE_MASK_MINUS)
/* Error return values for mode_compile. */
# define MODE_INVALID (struct mode_change *) 0
# define MODE_MEMORY_EXHAUSTED (struct mode_change *) 1
# define MODE_BAD_REFERENCE (struct mode_change *) 2
# ifndef PARAMS
# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
# define PARAMS(Args) Args
# else
# define PARAMS(Args) ()
# endif
# endif
struct mode_change *mode_compile PARAMS ((const char *, unsigned));
struct mode_change *mode_create_from_ref PARAMS ((const char *));
mode_t mode_adjust PARAMS ((mode_t, const struct mode_change *));
void mode_free PARAMS ((struct mode_change *));
#endif