Fully commented and explained version of the DIRSIZE program

Copyright 1998, Ted Davis, all rights reserved.
Code is emphasised; comments are not. Each emphasised entry, except for the last few, is separated from its neighbors by comments - if a section of emphasised text is continued on the next line, word wrap is at work: it should be read as one line. Each code line is followed by a blank line.

Suppress display of batch commands in the main program.
 @echo off

The branch of the file system to scan is passed as the only argument to this program - we need tp get a list of all of the directories and sub-directories: the following line does that, and puts the list in the file }{.dat, the name of which is completely arbitrary.
 dir %1 /a:d /b /s > }{.dat

Before we can give this file to the DATE command as a series of bad dates to be echoed back with an error message, we must add something at the end that DATE will accept - a blank line which causes DATE to terminate without resetting the date.
 echo.>> }{.dat

Now we can give that file to DATE as input and filter DATE's output to keep only the error message lines that have the directory name at the end - the output from the FIND filter goes into the file }{.SRC, the name of which is completely arbitrary.
 date < }{.dat | find "Enter " > }{.src

}{.SRC is going to be used as input to a secondary command processor through redirection - it is essential that the secondary command processor terminate, which requires an EXIT command, so we add one to the end of the file.
 echo exit>> }{.src

At this point, we have a file (}{.SRC) that contains a list of commands that the command processor understands - provided we make ENTER a valid command (which we will do later) followed by an EXIT command to make it terminate and return to this batch program. The directory name in the }{.SRC file follows the error message from the DATE command. Unfortunately, the error message has a different number of words in different operating systems so the number that eventually will be used to define the position of the directory name has to be a variable - we initialize it here to the value required for DOS and Win95.
 set arg=4

In order for NT4 to produce a directory listing in the same format (more or less) as that produced by DOS and Win95, we have to give the DIR command a special /x switch - the other operating systems consider that an error, so that too has to be a variable, initialized here to NULL for DOS and Win95.
 set sw=

Redirection arrows, as are used to build the ENTER.BAT file don't work inside quotes - since it is necessary to put one just before a redirection arrow, we have to make it appear only after the redirection operation has been completed. We have to place, not the quote mark itself, but a reference to a quote mark that will be substituted when ENTER.BAT is actually run.
 set mark="

To be sure of enough environment space, we have to use the /e=nnn switch to COMMAND.COM, but this is an error if the the command processor is CMD.EXE (NT4) - this must also be a variable, initialized here for DOS and Win95.
 set e=/e:1024

Now we see just what operating system we are using - it isn't necessary to distinguish between DOS and Win95, but it is necessary to detect NT4. In NT4, the OS environment variable is set to "Windows_NT", otherwise the variable is null - if the OS is NT, we need to change the values of the OS dependent variables. The directory name begins with the fifth field of the DATE error message in NT4, not the fourth as in DOS and Win95.
 if %os%!==Windows_NT! set arg=5

We need to use the /x switch for NT4,
 if %os%!==Windows_NT! set sw=/x

But the /e switch for COMMAND.COM can't be used with NT4's CMD.EXE, so we clear that variable.
 if %os%!==Windows_NT! set e=

Begin building the ENTER.BAT file. This file could have been created separately and just called, but that means that this program would require two files, one of which is sure to be lost as the program is passed around and moved from directory to directory. Keep in mind that the comments below refer to ENTER.BAT, not to the main program - they are simply ECHOed from this program to the ENTER.BAT file. When we need to put a reference to a variable, rather than the value of the variable, into the file, we have to double all '%' signs because the main command processor strips one level of '%' characters off at each pass - we need to leave the last level for when the ENTER.BAT program actually executes. When the secondary command processor processes the lines in }{.SRC that are the augmented error messages, it will attempt to execute first an internal command called ENTER, and not finding that, will attempt to execute ENTER.COM, ENTER.EXE, and ENTER.BAT in that order - we can't provide it with an executable program, but we can provide it with a batch file named ENTER, which we build below. ENTER.BAT runs in a secondary command processor, so we need to suppress display of its commands separately, to be sure that no command processor will decide to display them - if not suppressed, they would appear in the output file (RESULT.TXT)
 echo @echo off> enter.bat

If the argument that should contain the directory (or part of it) is null, then the line in }{.SRC that invoked this instance of ENTER.BAT contained no directory name, and the ENTER.BAT program should no nothing - if the argument is NULL, just skip to the end.
 echo if %%%arg%!==! goto end>> enter.bat

Clear the variable used to hold quote marks if the directory name contains one or more spaces - directory and file names with spaces must be quoted when used as arguments to commands, but directory names for real DOS must never be; if the name does not contain spaces, it doesn't matter in any of the three OSs. Note that there is no space between the '=' and the ">>" - this ensures that the variable will be deleted instead of being set to a space.
 echo set quote=>> enter.bat

Clear the variable that will hold the name of the directory. If not cleared, the names will just pile up, one at the end of the previous pile, until we run out of environment space.
 echo set dname=>> enter.bat

It is necessary to use a loop to rebuild the directory name if it contains spaces because it is spread out over several argument positions. This would not be necessary if there were a way to quote it before passing it as a bad date to the DATE command, but there isn't, so we have to rebuild the string. Note that this is not a general solution because there is no way to distinguish between spaces and other command line argument delimiters at this point - the necessary information has been irrecoverably lost. :loop is the label used as the target of a GOTO at the bottom of the loop that splices the elements of the name back together with spaces between elements.
 echo :loop>> enter.bat

On the first pass, the only pass if there are no spaces, this line sets the variable to nothing followed by the string in the Nth argument, where N is the field in work - initially either the fourth or fifth, depending on the OS.
 echo set dname=%%dname%%%%%arg%>> enter.bat

Move the argument list one place to the left so that the next argument (field, string) now has the same number as the original one.
 echo shift>> enter.bat

If the new field is blank, we have reached the end of the directory name and need not add a space at the end of the string in work - we need to jump to the :cont label that follows the end of the loop.
 echo if %%%arg%!==! goto cont>> enter.bat

The next line adds a space, the one referred to in the comment, to the end of the string in work.
 :: Note that there is a space following %%dname%% in the following line
 echo set dname=%%dname%% >>enter.bat


If a space has been added, it is necessary to quote the string when it is later passed to commands as arguments - this is done by setting the QUOTE variable to a double quote mark.
 echo set quote=%%mark%%>> enter.bat

Since we added a space, we have to add another element to the string, so we jump back up to the top of the loop. This is the last line in the loop.
 echo goto loop>> enter.bat

This places the target of the GOTO :cont command above in its proper place in the file.
 echo :cont>>enter.bat

Here we are ECHOing an ECHO command into the file. This ECHO command generates the first line of the report for each directory: the line with the directory name and a bit of a trick - the string in () contains " bytes" which will allow the lien to pass through the same FIND filter that passes the line that contains the size of the directory because that one is filtered on " byte".
 echo echo %%dname%% (size in bytes)>> enter.bat

This is a rather complicated bit of gibberish: when written to the ENTER.BAT file, one level of '%' will be removed, leaving "if exist %quote%%dname%\*.*%quote% etc." which will be expanded to (for names with spaces) 'if exist "foo bar'*.*" etc.' when ENTER.BAT runs ("foo bar" represents any directory name). Test for whether the directory is empty or not, if not, skip the directory listing because we already know what it is. This part doesn't work as expected in NT4 because self and parent directories (the '.' and ".." entries count as files to the DIR command (if the directory is not the root, it always contains at least two items seen by DIR under CMD.EXE).
 echo if exist %%quote%%%%dname%%\*.*%%quote%% dir %sw% %%quote%%%%dname%%%%quote%%>> enter.bat

If it is empty (DOS and Win95) just dummy in the response.
 echo if not exist %%quote%%%%dname%%\*.*%%quote%% echo 0 file(s) 0 bytes>> enter.bat

This :end label is the target of the bypass GOTO at the head of the ENTER.BAT file. This ends ENTER.BAT.
 echo :end>> enter.bat

Now we feed the script file we built earlier (}{.SRC) to the secondary command processor - we don't need to know whether it is COMMAND.COM or CMD.EXE, because the COMSPEC environment variable contains that part of the command line we wish to generate here. This is where the business that all of the above code has lead up to - this executes the DATE error messages followed by directory names to invoke ENTER.BAT once for each line and pass it the rest of the line as arguments. The output of the entire secondary command processor is then filtered to pass only those lines containing " bytes" (the name line and the line at the end of the directory listings that contains the number of bytes in the files in the directory being examined) and written into the target file, RESULT.TXT.
 %comspec% %e% < }{.src | find " bytes" | find /v " free">result.txt

From here out, it's just cleanup: delete the temporary files, but not the output file, and clear the environment variables used by the main program (there is no need to clear the ones used only by ENTER.BAT since they disappeared when the secondary command processor terminated.
 del }{.dat


 del }{.src


 del enter.bat


 set sw=


 set arg=


 set e=


 set mark=