SYNOPSIS: Treewalk - visit all the files in a directory tree. usage: treewalk [] [[command] ] options are: post, both, dir , verbose, single, filter options and "command" are only checked in the first 3 characters. DESCRIPTION: Treewalk is a command for visiting all the files of a subtree of an AmigaDOS file system. It's designed to be reasonably fast, robust, and not use a lot of stack space - or any other critical resources. About the only thing you're liable to run out of is memory. (Note: "all the files of a subtree" doesn't include the directory that's the root. So if you do something to every directory in the subtree, it won't do it to the root directory. The normal behavior of the command is to test every file in the specified subtree against a user "filter" expression, and if the file passes through the filter, to issue the user supplied command with that file as one of the arguments. Any argument that isn't an option starts the user supplied command. All arguments after the first command argument are concatenated together, with a single space between them, to make up the command. As each file is visited, its full path name enclosed in '"''s (so that things like RAM DISK: will work) is concatenated to the end of the command. When the last file is visited, or the command line becomes to long for the system, it is issued to the cli. The current working directory of the command is the directory in which the treewalk command was started, not the directory being visited or the directory that is the root of the tree being walked. The command is run as many times as needed so that all files are visited. The "command" keyword indicates that the next argument is the start of the command, whether or not it looks like a keyword. This is useful if the command you wish to run on the starts with a valid keyword. If there is no command, treewalk will print out the names of all files that pass through the filter. The "verbose" option causes treewalk to print the text of every command before issuing it. The "ignore" option causes treewalk to not print anything on error returns from the command. The "single" option defeats the stacking of files on the command line, and the command is run once for every file. This is useful if the command in question only accepts one filename. For instance, "list nohead" should be done this way. The "dir" option is followed by the name of a directory. That directory will then be used as the root of the tree, instead of the current directory. Normally, treewalk will visit a directory before it visits any of it's children. This is a "preorder" treewalk, "pre" because it visits parents first. You can change this with the following options. The "post" option causes treewalk to visit all the children of a directory before it visits that directory. The "both" option causes treewalk to visit a directory both before and after it visits all the children of that directory. If the directory has no subdirectories, then it will list all the files of that directory twice, one listing following immediately after another. The "filter" option is used to specify the filter expression. If no filter is provided, all files in the subtree will pass. The filter expression is similar to a C language expression, Like a C expression, it is false if it evaluates to 0, and true otherwise. The only variables in the expression are parts of the FileInfoBlock of the file being evaluated. They are: diskkey - for gurus only direntrytype - ditto entrytype - ditto filename - the name of the file in this directory (string value) name - the full path name of the file (string value) protection - the protection mask size - the number of bytes in the file numblocks - the number of blocks in the file date - date the file was last changed day - day the file was last changed comment - comment associated with the file (string value) The "day" version of date ignores the minutes of the day of creation, so that it will compare equal to a day with no time specified via the "`" construct. I.e., "day == `yesterday`" matches files created yesterday. Note to purists: The "date" value is the number of minutes since midnight, January 1, 1978. It's called "date" instead of "datestamp" because it's not a real datestamp. The protection bits `rewd` are inverted on output, as they have inverted meaning: the `r` bit being on means you _can't_ read the file. List prints a `r` when you can read the file. These bits are inverted to prevent the user from having to remember this. The FileInfoBlock values can also be gotten for other files. The same names as above are used, preceded by a file name and a '.'. For instance, main.c.size is the size of the file "main.c" in the directory treewalk was started in. Such expressions are evaluated at startup time, and are constant throughout the execution of treewalk. The other valid constants in filter expressions are: true - longhand for 1 false - longhand for 0 integers - The same as C: starting with a '0' indicates octal starting with a '0x' indicates hex otherwise, it's decimal "string" - a text string 'string' - ditto In addition any string surrounded by "`"'s is an integer constant, of one of two forms. If it consists of a string of characters from the set "rwdespah", then the appropriate bits will be turned on to match the protection of the file. Otherwise, this may be a date and time specifier. These are of the form "day minute". Day is either a fully specified numeric date of the form "month/day/year", a day of the week, "yesterday", or not specified. The "month/day/year" form uses a two-digit year between 1978 & 1999. The day of the week refers to the previous day of that name. If the day is not specified, then today is used. Time is either "noon", hour:minute on a 24-hour clock, or missing. If the time is missing, then midnight is assumed. If the day specified is today, and the time is later than the current time, then yesterday is assumed - even if you specify the day explicitly. There are some operands that depends on the file currently being filtered, or on the user. They are: dir - true if the current file is a directory, else false file - the opposite of dir name - the full path name of the file (string value) user - name is printed on the console, followed by a ?, and a line is read. If the first character of the line is 'y' or 'Y', then true, otherwise false. Finally, any non-string not recognized is assumed to be an ARexx macro name. The extension used in searching for the macro is "ftw". This macro will be invoked as a function with pathname of the directory as it's first argument, and the file name as it's second. The pathname will be in a form such that "arg(1) || arg(2)" will be the full pathname. The expression the macro returns should be an integer, and will be used as the value of the macro token in the expression. The macro may issue "commands" back to the running treewalk. The commands are treated as filters, and the current file is sent through them, and the result returned. What the result is depends on whether you have specified "options results". If this is not on, the rc is 0 if the filter returns "true", and 1 if the filter returns "false". If the results option is on, and no errors occured, rc is always 0, and the result string is the value, formated as four bytes of hex. You can then use x2c() and import() to extract strings, if the value returned was a string (i.e. - you issued the command 'comment'). In either case, return codes greater than 1 indicate a real error - usually in the filter expression, sometimes that you're out of memory. As a suggestion for writing ftw macros, I recommend following the rule that if the macro uses the file name, it returns a 1, and if it rejects the file name, it returns a 0. This allows treewalk to print the names of the files used by the macro by 'treewalk filter macro', or to not print the names by 'treewalk filter "macro & false"'. Of course, if the macro is just a filter itself, then it should return either 0 or 1. To combine these, there are the operators. All but four are from C, and the precedence of the operators is the same as C. They are: ! - unary logical not; ~ - unary bitwise not & - bitwise and % - bitwise exclusive or | - bitwise or =*, =#, !*, !# - string compares <, >, <=, >=, == - integer comparisons && - logical and, short circuiting || - logical or, short circuiting (, ) - parens for grouping The only four operators not found in C are =*, =#, !* and !#. These are pattern matching operators for use on strings (file names or comments). The left-hand operand is a string, the right-hand operand is a pattern. =* and !* provides Unix-like regular expressions, with '*' and '?' having special meaning. =# and !# provides AmigaDOS like regular expressions, with '#' and '?' having special meanings. =* and =# are true if the comparisons match, and !* and !# are true if the comparisons don't match. The rest of the operators behave as one would expect them to. The only thing that needs to be pointed out is that logical and (or) short circuits. So if the left-hand operand is false (true), the right-hand operand is never evaluated. This allows a limited form of flow control in the filter expression. Like C, it is legal to apply the logical operators to non-boolean ints, and to apply the comparison operators to booleans. Unlike C, strings can not have their addresses extracted, and ints cannot be used for string addresses. Strings may only appear, and are all that may appear, as the operands of the pattern matching operators. As a final suggestion on filters, unless the filter is a single token, it's probably best to surround it in '"''s, as that will cause the magic characters in the expression (&, |, <, > come to mind) to not be interpreted by whatever CLI you are running. EXAMPLES: List all the directories in src: treewalk dir src: filter dir Delete the subtree x: treewalk dir x post single delete This isn't really very useful, as "delete all" is faster. However, it illustrates several points. First, "delete" is broken, as it has a limited number of arguments. Second, this requires the postorder treewalk, so that all the files in a directory will be deleted before the directory is deleted. Delete all .o files in the current subtree: treewalk filter "filename =# '#?.o'" single delete List all files newer than :last-backup. treewalk filter "date > :last-backup.date" Find all files on the current device older than :last-backup using over 20 blocks, that don't have the execute bit on, and compress them. treewalk dir : filter "date < :last-backup.date && numblocks > 20 && !(protection & `e`)" compress Same as above, except we also don't compress .o files in the subtree at :go and we only compress if the user wants us to. treewalk dir : filter "name !* '*:go/*' && date < :last-backup.date && numblocks > 20 && !(protection & `e`) && user" compress This last might more efficiently be done as a Rexx macro that decides whether or not the file will be compressed, and then asks the user. Let's assume we've got a macro "munchp.ftw" that does the name, mode and size checking. You could then do: treewalk dir : filter "date < :last-backup.date && munchp && user" compress As an alternative, suppose that instead of compressing them, you wanted to save them to an archive somewhere, and had a rexx script to do that. Further, suppose that the Rexx script (archive.ftw) return 1 if it did the archive, and 0 if it didn't. Then you might do: treewalk dir : filter "date < :last-backup.date && munchp && archive && false" To quietly do the archiving. Or you might do: treewalk >logs:archive.log dir : filter "date < :last-backup.date && munchp && archive" To list the archived files to logs:archive.log. Copyright (C) 1989 Mike Meyer This file is free documentation; 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 1, or 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., 675 Mass Ave, Cambridge, MA 02139, USA.