Search code examples
batch-filemathcmdsubtraction

Batch File Math (Subtraction) Stopped Working


I know that there must be some syntax issue on my end with this, but I can't for the life of me figure this out right now. This is a script I put together a few months back to automatically download some files for me as they release every month. It was working fine, however this month I noticed that nothing was downloading. After some troubleshooting and investigation, I found that the month variable was setting itself to "-1" instead of subtracting "1" from it's current value. As a result of this I'm unable to get the proper file name to attempt the download. It's the same match that I'm using on days, revision number, and year number, but for some reason the month variable just isn't cooperating with me and I can't figure out why.

    :MASTER
        @echo off
        mode con:cols=100 lines=5
    ::Setup First Download
        for /f "tokens=2*" %%a in ('REG Query "HKLM\SOFTWARE\Wow6432Node\ExampleRegistryKey" /v ExampleString 2^>nul') do set "ExampleDir=%%~b"
        pushd "%ExampleDir%"
        for /f "tokens=2 delims==" %%a in ('findstr SQLiteHome Example.ini') do set SQLiteHome=%%a
        ::Revision Number
        set num=17
        set /a "num=num-1"
        ::Begin set date
        for /f "tokens=1-4 delims=/-. " %%i in ('date /t') do (call :set_date %%i %%j %%k %%l)
        goto :end_set_date
        :set_date
        if "%1:~0,1%" gtr "9" shift
        for /f "skip=1 tokens=2-4 delims=(-)" %%m in ('echo,^|date') do (set %%m=%1&set %%n=%2&set %%o=%3)
        goto :eof
        :end_set_date
        set dd=31

This section here is where the month starts to equal -1 instead of current one subtracted from the current month.

        set /a "mm=mm-1"
        if %mm%==9 set mm=09
        if %mm%==8 set mm=08
        if %mm%==7 set mm=07
        if %mm%==6 set mm=06
        if %mm%==5 set mm=05
        if %mm%==4 set mm=04
        if %mm%==3 set mm=03
        if %mm%==2 set mm=02
        if %mm%==1 set mm=01
        if %mm%==0 set /a "yy=yy-1"
        if %mm%==0 set mm=12
        Set EarlyEntry=early_up_sqlite_r
        Set FullEntry=update_sqlite_r
        Set MstarEntry=mstar_ext_sqlite_r
        Set PSNEntry=psn_ext_sqlite_r
        set CurrentEntry=%EarlyEntry%
        set ThisFile=%CurrentEntry%%num%_%yy%%mm%%dd%.exe
        ECHO %ThisFile%
        set INIentry=EarlyLast
    ::Create %WorkingDirectory%\Dates.txt and Downloads.ini
        set INIfile=C:\ProgramData\SA_Updater\Downloads.ini
        set WorkingDirectory=C:\ProgramData\SA_Updater\
        set OldDownloads=C:\ProgramData\SA_Updater\Downloads\
        set PSNini=PSNLast
        set MSTARini=MSTARLast
        set Fullini=FullLast
        set Earlyini=EarlyLast
        echo %PSNini%
        echo Dates > %WorkingDirectory%\Dates.txt
        if not exist "%WorkingDirectory%" mkdir "%WorkingDirectory%"
        if not exist "%OldDownloads%" mkdir "%OldDownloads%"
        pusd %WorkingDirectory%
        if not exist "%INIfile%" (
        ECHO %PSNini%= > %INIfile%
        ECHO %MSTARini%= >> %INIfile%
        ECHO %Fullini%= >> %INIfile%
        ECHO %Earlyini%= >> %INIfile%
        )
    :ObtainVariables
        ::Find the Last version downloaded

This is where I've kept a log for the download attempts echo Dates > %WorkingDirectory%\Dates.txt The output is looking like this:

Dates 

http://Example.com/updates/early_up_sqlite_r16_2018-131.exe 
http://Example.com/updates/early_up_sqlite_r15_2018-131.exe 
http://Example.com/updates/early_up_sqlite_r14_2018-131.exe 

Instead of:

Dates 

http://Example.com/updates/early_up_sqlite_r16_20180731.exe 
http://Example.com/updates/early_up_sqlite_r15_20180731.exe 
http://Example.com/updates/early_up_sqlite_r14_20180731.exe

The rest of the script:

            for /f "tokens=2 delims==" %%a in ('findstr %INIentry% %INIfile%') do set LastINIfile=%%a
            ECHO %LastINIfile%
        ::Revision Number
            set num=17
            set /a "num=num-1"
        ::Begin set date
            for /f "tokens=1-4 delims=/-. " %%i in ('date /t') do (call :set_date %%i %%j %%k %%l)
            goto :end_set_date
            :set_date
            if "%1:~0,1%" gtr "9" shift
            for /f "skip=1 tokens=2-4 delims=(-)" %%m in ('echo,^|date') do (set %%m=%1&set %%n=%2&set %%o=%3)
            goto :eof
            :end_set_date
            set dd=31
            set /a "mm=mm-1"
            if %mm%==9 set mm=09
            if %mm%==8 set mm=08
            if %mm%==7 set mm=07
            if %mm%==6 set mm=06
            if %mm%==5 set mm=05
            if %mm%==4 set mm=04
            if %mm%==3 set mm=03
            if %mm%==2 set mm=02
            if %mm%==1 set mm=01
            if %mm%==0 set /a "yy=yy-1"
            if %mm%==0 set mm=12
            set ThisFile=%CurrentEntry%%num%_%yy%%mm%%dd%.exe
            goto Download
    :Download
        ::Setup Download Variables
            cls
            set Download=http://Example.com/updates/%ThisFile%
            GOTO TryDownload
    :TryDownload
        ::Add Download Attempt to %WorkingDirectory%\Dates.txt
            echo %Download% >> %WorkingDirectory%\Dates.txt
        ::Is the file download? If so, start the install
            cls
            mode con:cols=100 lines=10
            powershell "Import-Module BitsTransfer; Start-BitsTransfer '%Download%' '%DownloadDir%'"
            mode con:cols=100 lines=5
            cls
            ::ping localhost -n 3 >nul
            if exist C:\ProgramData\SA_Updater\%ThisFile% (
            GOTO StartInstall
            ) else (
        ::Try Downloading
            GOTO TryAgain
            )
            cls
    :TryAgain
        set /a "num=num-1"
        if %mm%==9 set mm=09
        if %mm%==8 set mm=08
        if %mm%==7 set mm=07
        if %mm%==6 set mm=06
        if %mm%==5 set mm=05
        if %mm%==4 set mm=04
        if %mm%==3 set mm=03
        if %mm%==2 set mm=02
        if %mm%==1 set mm=01
        if %num%==0 set /a "dd=dd-1"
        if %dd%==27 set /a "mm=mm-1"
        if %mm%==0 set /a "yy=yy-1"
        if %mm%==0 set mm=12
        if %dd%==27 set dd=33
        if %num%==0 set num=17
        set ThisFile=%CurrentEntry%%num%_%yy%%mm%%dd%.exe
        set Download=http://Example.com/updates/%ThisFile%
        set DownloadDir=%WorkingDirectory%%ThisFile%
        if %ThisFile% EQU %LastINIfile% GOTO CheckEarly
        GOTO TryDownload
        )
    :StartInstall
        %WorkingDirectory%%ThisFile% /w /v"INSTALLPREREQUISITES=0"
        robocopy %WorkingDirectory% %OldDownloads% %ThisFile%
        pushd %WorkingDirectory%
        del %ThisFile%
        del /Q /A H *.tmp
        GOTO DoINIstuff
    ::Update ThisFile
        :CheckEarly
            if %CurrentEntry% EQU %EarlyEntry% GOTO SetupFull
            GOTO CheckFull
        :CheckFull
            if %CurrentEntry% EQU %FullEntry% GOTO SetupMstar
            GOTO CheckMstar
        :CheckMstar
            if %CurrentEntry% EQU %MstarEntry% GOTO SetupPSN
            GOTO CheckPSN
        :CheckPSN
            if %CurrentEntry% EQU %PSNEntry% GOTO SetupEarly
            GOTO CheckEarly
    ::Setups
        :SetupFull
            set CurrentEntry=%FullEntry%
            set INIentry=%Fullini%
            GOTO ObtainVariables
        :SetupMstar
            set CurrentEntry=%MstarEntry%
            set INIentry=%MSTARini%
            GOTO ObtainVariables
        :SetupPSN
            set CurrentEntry=%PSNEntry%
            set INIentry=%PSNini%
            GOTO ObtainVariables
        :SetupEarly
            set CurrentEntry=%EarlyEntry%
            set INIentry=%Earlyini%
            GOTO ObtainVariables
    :DoINIstuff
        SetLocal EnableDelayedExpansion
        Set _PathtoFile=%INIfile%
        Set _OldLine=%INIentry%=
        Set _NewLine=%INIentry%=%ThisFile%
        Call :_Parse "%_PathtoFile%"
        Set _Len=0
        Set _Str=%_OldLine%
        Set _Str=%_Str:"=.%987654321
        :_Loop
        If NOT "%_Str:~18%"=="" Set _Str=%_Str:~9%& Set /A _Len+=9& Goto _Loop
        Set _Num=%_Str:~9,1%
        Set /A _Len=_Len+_Num
        PushD %_FilePath%
        If Exist %_FileName%.new Del %_FileName%.new
        If Exist %_FileName%.old Del %_FileName%.old
        Set _LineNo=0
        For /F "Tokens=* Eol=" %%I In (%_FileName%%_FileExt%) Do (
        Set _tmp=%%I
        Set /A _LineNo+=1
        If /I "!_tmp:~0,%_Len%!"=="%_OldLine%" (
        >>%_FileName%.new Echo %_NewLine%
        ) Else (
        If !_LineNo! GTR 1 If "!_tmp:~0,1!"=="[" Echo.>>%_FileName%.new
        SetLocal DisableDelayedExpansion
        >>%_FileName%.new Echo %%I
        EndLocal
        ))
        Ren %_FileName%%_FileExt% %_FileName%.old
        Ren %_FileName%.new %_FileName%.ini
        PopD
        Goto :CheckEarly
        :_Parse
        Set _FilePath=%~dp1
        Set _FileName=%~n1
        Set _FileExt=%~x1
        Goto :EOF

I hope I haven't omitted too much to figure this out. Basically, I can see that the revision number and the day are acting like they should, but the month just reverts to "-1" instead of actually performing the subtraction, and I'm basically pulling my hair out trying to figure out what I'm doing wrong.


Solution

  • The reason is quite simple. A number string with a leading 0 is interpreted as octal number by C function strtol used by cmd.exe to convert a number string to an integer.

    08 and 09 are invalid numbers in octal numeral system and for that reason function strtol returns 0 which is subtracted next by 1 resulting in -1.

    The simple solution is using set /a "mm=1%mm%-101" instead of set /a "mm=mm-1". Then first the month value is concatenated as string with the character 1 building the strings 101 to 112 and so the number string has no leading 0 anymore and from this number 101 is subtracted to get 0 to 11 assigned as string to environment variable mm.

    By the way: Use the following two command lines to get back the leading zero after subtraction:

    set "mm=0%mm%"
    set "mm=%mm:~-2%"
    

    The first line concatenates 0-11 to 00-011 and the second line takes just the last two characters of this string resulting in 00-11 assigned finally to environment variable mm.

    The next two lines should be replaced by: if %mm% == 00 set "mm=12" & set /a "yy-=1"