Here's my script, roughly tested:
/****************************************************************************
* findmissingfilenames.cmd
*
* Version: 2024-04-21
*
* Syntax: findmissingfilenames <filemask> <startnum> <endnum> [<logfile>]
*
* o <filemask> must have a part with '???' chars, where the amount of it
* depends on the range <startnum> to <endnum>.
* o <filemask> may include a path.
* o <startnum> and <endnum> must be numbers.
* o <logfile> may optionally be specified. The default <logfile> is
* 'missing.log'.
* o Every arg may be surrounded with double quotes.
*
* Examples:
* findmissingfilenames 1????.pdf 1 500
* findmissingfilenames 10???.pdf 1 500
*
* By Andreas Schnellbacher
****************************************************************************/
/* Some header lines are used as help text */
HelpStartLine = 6
HelpEndLine = 18
'@ECHO OFF'
CALL SETLOCAL
/* ----------------- Standard CMD initialization follows ----------------- */
SIGNAL ON HALT NAME Halt
SIGNAL ON NOVALUE NAME RexxError
SIGNAL ON SYNTAX NAME RexxError
SIGNAL ON FAILURE NAME RexxError
/*
SIGNAL ON ERROR NAME RexxError /* Causes doubled error messages */
SIGNAL ON NOTREADY NAME RexxError /* Causes error at encoding */
*/
env = 'OS2ENVIRONMENT'
TRUE = (1 = 1)
FALSE = (0 = 1)
CrLf = '0d0a'x
Redirection = '>NUL 2>&1'
PARSE SOURCE . . ThisFile
GlobalVars = 'env TRUE FALSE CrLf Redirection ERROR. ThisFile'
/* Some OS/2 Error codes */
ERROR.NO_ERROR = 0
ERROR.INVALID_FUNCTION = 1
ERROR.FILE_NOT_FOUND = 2
ERROR.PATH_NOT_FOUND = 3
ERROR.ACCESS_DENIED = 5
ERROR.NOT_ENOUGH_MEMORY = 8
ERROR.INVALID_FORMAT = 11
ERROR.INVALID_DATA = 13
ERROR.NO_MORE_FILES = 18
ERROR.WRITE_FAULT = 29
ERROR.READ_FAULT = 30
ERROR.SHARING_VIOLATION = 32
ERROR.GEN_FAILURE = 31
ERROR.INVALID_PARAMETER = 87
ERROR.ENVVAR_NOT_FOUND = 204
rc = ERROR.NO_ERROR
CALL RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
CALL SysLoadFuncs
/* ----------------- Standard CMD initialization ends -------------------- */
GlobalVars = GlobalVars 'rc LogFile HelpStartLine HelpEndLine'
DO once = 1 TO 1
DefaultLogFile = 'missing.log'
/* Parse args */
PARSE ARG Args
Args = STRIP( Args)
/* Show help */
IF Args = '' THEN
CALL ShowHelp
ELSE
DO
IF WORDPOS( Args, '-? /?') > 0 THEN
CALL ShowHelp
END
/* Parse and check arg words */
rest = Args
LogFile = ''
a = 0
DO WHILE rest <> ''
a = a + 1
rest = STRIP( rest)
IF LEFT( rest, 1) = '"' THEN
PARSE VAR rest '"'next'"' rest
ELSE
PARSE VAR rest next rest
SELECT
WHEN a = 1 THEN
DO
FileMask = next
IF FileMask = '' THEN
DO
rc = ERROR.INVALID_PARAMETER
CALL ExitError 'Error: filemask not specified.'
END
END
WHEN a = 2 THEN
DO
StartNum = next
IF StartNum = '' THEN
DO
rc = ERROR.INVALID_PARAMETER
CALL ExitError 'Error: startnum not specified.'
END
IF DataType( StartNum) <> 'NUM' THEN
DO
rc = ERROR.INVALID_PARAMETER
CALL ExitError 'Error: startnum is not a number.'
END
END
WHEN a = 3 THEN
DO
EndNum = next
IF EndNum = '' THEN
DO
rc = ERROR.INVALID_PARAMETER
CALL ExitError 'Error: endnum not specified.'
END
IF DATATYPE( EndNum) <> 'NUM' THEN
DO
rc = ERROR.INVALID_PARAMETER
CALL ExitError 'Error: endnum is not a number.'
END
END
WHEN a = 4 THEN
LogFile = next
OTHERWISE
rc = ERROR.INVALID_PARAMETER
CALL ExitError 'Error: Too many args specified.'
END
END
IF LogFile = '' THEN
LogFile = DefaultLogFile
/* Find number of contiguous '?' chars in FileMask */
NumPartLen = 0
NumPartPos = 0
pStart = 1
PrevChar = '?'
DO n = 1 TO LENGTH( FileMask)
ThisChar = SUBSTR( FileMask, n, 1)
IF ThisChar <> '?' THEN
ITERATE
IF PrevChar <> '?' THEN
LEAVE
/* Save pos of first '?' char */
IF NumPartLen = 0 THEN
NumPartPos = n
/* Advance length */
NumPartLen = NumPartLen + 1
PrevChar = ThisChar
END
/* Check number of contiguous '?' chars in FileMask */
IF NumPartLen < LENGTH( StartNum) THEN
DO
rc = ERROR.INVALID_PARAMETER
CALL ExitError 'Error: Number of ''?'' chars in filemask too low for startnum.'
END
IF NumPartLen < LENGTH( EndNum) THEN
DO
rc = ERROR.INVALID_PARAMETER
CALL ExitError 'Error: Number of ''?'' chars in filemask too low for endnum.'
END
/* Delete old LogFile */
IF FileExist( LogFile) THEN
CALL SysFileDelete LogFile
/* Process range of numbers */
m = 0
f = 0
DO n = StartNum TO EndNum
/* Build Filename */
NumPart = RIGHT( n, NumPartLen, '0')
Filename = OVERLAY( NumPart, FileMask, NumPartPos, NumPartLen)
f = f + 1
/*SAY RIGHT( f, NumPartLen)': 'Filename*/
/* Search filename */
IF \FileExist( Filename) THEN
DO
m = m + 1
/* Write to LogFile */
CALL WriteLog 'Missing: 'Filename
END
END
/* Close LogFile if opened */
IF STREAM( LogFile) = 'READY' THEN
CALL STREAM LogFile, 'C', 'CLOSE'
/* Report number of missing files */
IF m = 0 THEN
SAY 'All 'f' numbers exist.'
ELSE
SAY m' file(s) of 'f' are missing. See 'LogFile'.'
END
EXIT( rc)
/* ======================================================================= */
/* ----------------------------------------------------------------------- */
FileExist: PROCEDURE EXPOSE (GlobalVars)
rc = ERROR.NO_ERROR
PARSE ARG Filename
IF FileName = '' THEN
RETURN( 0)
ELSE
RETURN( STREAM( Filename, 'C', 'QUERY EXISTS') <> '')
/* ----------------------------------------------------------------------- */
WriteLog: PROCEDURE EXPOSE (GlobalVars)
PARSE ARG Message
StreamState = STREAM( LogFile)
IF WORDPOS( StreamState, 'UNKNOWN NOTREADY') > 0 THEN
DO
StreamState = STREAM( LogFile, 'C', 'OPEN')
PARSE VAR StreamState StreamState':'
END
IF StreamState = 'READY' THEN
CALL LINEOUT LogFile, Message
RETURN( '')
/* ----------------------------------------------------------------------- */
ShowHelp: PROCEDURE EXPOSE (GlobalVars)
DO l = HelpStartLine TO HelpEndLine
SAY SUBSTR( SOURCELINE( l), 3)
END
EXIT rc
/* ----------------------------------------------------------------------- */
ExitError: PROCEDURE EXPOSE (GlobalVars)
PARSE ARG Message
CALL ShowError Message
EXIT rc
/* ----------------------------------------------------------------------- */
/* Show error message in a message box and/or write to STDERR */
ShowError: PROCEDURE EXPOSE (GlobalVars)
PARSE ARG Message
CALL LINEOUT 'STDERR', Message
RETURN( '')
/* ----------------------------------------------------------------------- */
Halt:
CALL ShowError 'Interrupted by user.'
EXIT( 99)
/* ----------------------------------------------------------------------- */
/* Give a standard REXX error message. */
/* This is for REXX error conditions only. */
/* System error codes and REXX error codes are different. */
RexxError:
/* SIGL must be saved to not get overwritten later. */
ErrorLine = SIGL
/* As an extension to the standard REXX error messages, */
/* the error condition will be appended to the error text. */
ConditionText = 'Condition: 'CONDITION( 'C')
ConditionDescription = CONDITION( 'D')
IF ConditionDescription <> '' THEN
ConditionText = ConditionText', Reason: 'ConditionDescription
ErrText = ''
IF SYMBOL( 'rc') = 'VAR' THEN
DO
IF rc > 0 & rc < 100 THEN
ErrText = ERRORTEXT( rc)
END
IF ErrText = '' THEN
ErrText = ConditionText
ELSE
ErrText = ErrText', 'ConditionText
/* Ensure that rc is set and that rc <> 0 is returned */
IF SYMBOL( 'rc') = 'VAR' THEN
DO
IF \( rc > 0 & rc < 100) THEN
rc = 999
END
ELSE
rc = 999
ErrorMessage = ''
IF ErrorLine > 0 THEN
ErrorMessage = RIGHT( ErrorLine, 6)' +++ 'SOURCELINE( ErrorLine)
IF ErrorMessage <> '' THEN
ErrorMessage = ErrorMessage''CrLf
ErrorMessage = ErrorMessage ||,
'REX'RIGHT( rc, 4, 0)': Error 'rc' running 'ThisFile', line 'ErrorLine': 'ErrText
CALL ShowError ErrorMessage
EXIT( rc)