5

I wanted to relive the nostalgia of good old dos.
I've been testing a few batch commands but I've noticed /a isn't usable in dos.
Is there another method I can use to add two variables without including /a?

@echo off
::this is how I would originally add two numbers
set /p number1=
set /p number2=
set /a "number1=number1+number2"
echo %number1%
pause >nul
exit

dos states "invalid switch. - /a" when I run this program.

Til
  • 5,150
  • 13
  • 26
  • 34
Zennik99
  • 51
  • 5

3 Answers3

4

It's a bit more tricky than using set /a, but it can be solved with MS-DOS 6.22.

One part is to write a function to add single digits and one function that can add multiple digits.

The main problem is to split a number into single digits, because MS-DOS doesn't support string manipulation.
But it exists a tiny flaw in the FOR-Loop handling, / splits a text into three parts.

for %%a in (`abcdef/ghijklmno`) do echo %%a

Outputs

abcdef
g
hijklmno

With this trick a number can be split into single digits

split.bat

@echo off
for %%P in (/%1.) do if %%P==: goto %1

set _idx=
set _remain=%2
set _splitRev=
set _splitRev_comma=

:loop

set _loop=1

for %%a in (/%_remain%) do call %0 :split %1 %%a
if NOT "%_remain%"=="" goto :loop
set %1=%_splitRev%
set %1_comma=%_splitRev_comma%

REM Clear temp vars
FOR %%v in (_remain _idx _loop _splitRev _splitRev_comma) do set %%v=
goto :eof

:split
if %_loop%%==2 goto :split_2
set _loop=2
set _remain=
set _splitRev=%3%_splitRev%
set _splitRev_comma=%3,%_splitRev_comma%
goto :eof

:split_2
set _remain=%3
goto :eof

:eof

And the add.bat looks like

@echo off
for %%P in (/%1.) do if %%P==: goto %1

call split _valueRev1 %1
call split _valueRev2 %2

set _result=
set _carry=
for %%d in (%_valueRev1_comma%,0,0,0,0,0) do call %0 :getDig1 %%d


REM Remove leading zeros
:zeroLoop
for %%z in (/%_result%) do set _remain=%%z
if not %_result%==0%_remain% goto :finish
set _result=%_remain%
goto :zeroLoop

:finish
echo %1+%2=%_result%

REM Clear temp vars
FOR %%v in (_result _carry _len _digit1 _digit2 _remain _valueRev1 _valueRev1_comma _valueRev2 _valueRev2_comma) do set %%v=
goto :eof

:getDig1
set _digit1=%2
set _digit2=
for %%d in (/%_valueRev2%0) do call %0 :getDig2 %%d

set _len=%_carry%
call %0 :lenAddDigit %_digit1%
call %0 :lenAddDigit %_digit2%
call %0 :len2val
set _result=%_val%%_result%
goto :eof

:getDig2
if not "%_digit2%"==" set _valueRev2=%2
if "%_digit2%"=="" set _digit2=%2
goto :eof

:lenAddDigit
if %2==1 set _len=%_len%#
if %2==2 set _len=%_len%##
if %2==3 set _len=%_len%###
if %2==4 set _len=%_len%####
if %2==5 set _len=%_len%#####
if %2==6 set _len=%_len%######
if %2==7 set _len=%_len%#######
if %2==8 set _len=%_len%########
if %2==9 set _len=%_len%#########
goto :eof

:len2val
set _carry=
set _val=
if %_len%.==. set _val=0
if %_len%.==. goto :eof
if %_len%==# set _val=1
if %_len%==## set _val=2
if %_len%==### set _val=3
if %_len%==#### set _val=4
if %_len%==##### set _val=5
if %_len%==###### set _val=6
if %_len%==####### set _val=7
if %_len%==######## set _val=8
if %_len%==######### set _val=9
if NOT "%_val%"=="" goto :eof
set _carry=#

if %_len%==########## set _val=0
if %_len%==########### set _val=1
if %_len%==############ set _val=2
if %_len%==############# set _val=3
if %_len%==############## set _val=4
if %_len%==############### set _val=5
if %_len%==################ set _val=6
if %_len%==################# set _val=7
if %_len%==################## set _val=8
if %_len%==################### set _val=9
goto :eof

:eof

Tested successfully on a MS-DOS 6.22 (VMWare)

#Limitations of MS-DOS 6.22

  1. IF doesn't support ELSE

    Workaround:

     IF %1==b echo It is equal
     IF NOT %1==b echo It isn't equal
    
  2. Only goto can jump to a label, CALL can only start another batch.

    Workaround:

    Put something like this into the first line of your batch

     FOR %%P in (/%1) do IF %%P==: goto %1
     ...
     REM This calls the current batch file and jumps to a label
     CALL %0 :myLabel arg1
     ...
     :myLabel
     echo arg1=%2
     echo Action1
     echo Action2
     goto :eof
    
  3. No code blocks, like

     FOR %%a in (1 2 3 ) do ( 
       set concat=%%a
       echo %concat%
     )
    

    Workaround:

     FOR %%a in (1 2 3 ) do CALL %0 :myFunc %%a
    
  4. No indirect expansion of variables

    Workaround:

     set var=content
     set indirect=var
      > temp$$$.bat echo set result=%%%indirect%%%
     call temp$$$.bat
     echo result=%result%
    
jeb
  • 78,592
  • 17
  • 171
  • 225
  • @aschipfl Thanks, but it seems to be the only `string handling` in MsDos6.22 and it can't split delimiters from a string, like `,;=`. I added some other limitations and possible workaounds – jeb Jan 17 '19 at 12:08
3

If you're trying to do this from actual DOS (and not Windows's emulation from the 32-bit era), it's simply not possible, unless you manually handle each possible pair of numbers you could have as input (which grows unmanageable when you go above the single-digits).

This was always a large deficiency in DOS's batch files, which was usually remedied by calling small scripts in actual scripting languages (such as BASIC), often being written out by the same .bat file that called them. This, of course, requires having an interpreter for your language of choice.

aaaaaa123456789
  • 5,541
  • 1
  • 20
  • 33
  • IMHO an "answer" that indicate "you can not do that" is not a good answer, barely a comment... **`:(`** – Aacini Jan 12 '19 at 21:28
  • 4
    @Aacini "Your problem has no solution" is as best as an answer can get here. Comments aren't meant to last (the policy has always been that they may be removed at any time, even if they usually aren't), and "you cannot do that" really _is_ the answer here. Particularly, considering that we're talking about an OS that was discontinued over two decades ago, the answer won't change, and thus "you can do that" is not only the only valid answer now, but the only valid answer forever. "No solution" _is_ an answer; it's completely different from "I don't know" or "not recommended". – aaaaaa123456789 Jan 13 '19 at 13:54
1

This is as close as I could get. Given the main script is called sum.bat provide the two numbers as command line arguments.

Here is the code of sum.bat:

@echo off & > nul ver
rem // Define constants here:
set SUB=.\sum-sub.bat
set FILE=.\sum.tmp
set RESULT=.\sum-res.tmp
rem // Check if enough arguments are provided:
if "%2"=="" (>&2 echo ERROR: too few arguments!) & < nul find "" & goto :END
rem // Initialise variables:
set LIST=
rem // Create list of as many space-separated `#` symbols as given by 1st number:
call %SUB% %1
rem // Append list by as many space-separated `#` symbols as given by 2nd number:
call %SUB% %2
rem // Terminate execution in case of unsupported numbers:
if ErrorLevel 1 goto :END
rem // Create empty temporary file:
> nul copy /Y nul %FILE%
rem // Fill temporary file with as many bytes as list items are given:
for %%I in (%LIST%) do (> nul copy /B %FILE% + nul %FILE% /A)
rem // Get size of temporary file, filter out first summary line and store in file:
dir /A:-D /-W /-C %FILE% | find "1 File(s)" > %RESULT%
rem /* Read from file the summary line but display it without "1 File(s)" prefix;
rem    since this is searched literally, the code becomes language-dependent;
rem    the "bytes" suffix unfortunately remains: */
< %RESULT% (
    for %%I in (# # # # # # # # # # # # # # # # # # # # # # # #) do > nul pause
    sort & echo/
)
rem // Clean up temporary files:
del %FILE% %RESULT%
:END

Here is the code of the sub-routine sum-sub.bat:

@echo off
rem // Jump to correct entry point to create/append a list with correct length:
2> nul goto :$%1 & < nul find "" & >&2 echo ERROR: unexpected argument!
rem // Do not add anything to the list upon errors:
goto :$0
rem /* Inverse list to add as many space-separated `#` symbols as given by the argument;
rem    extend it in the same manner in order to support numbers greater than `12`: */
:$12
set LIST=%LIST% #
:$11
set LIST=%LIST% #
:$10
set LIST=%LIST% #
:$9
set LIST=%LIST% #
:$8
set LIST=%LIST% #
:$7
set LIST=%LIST% #
:$6
set LIST=%LIST% #
:$5
set LIST=%LIST% #
:$4
set LIST=%LIST% #
:$3
set LIST=%LIST% #
:$2
set LIST=%LIST% #
:$1
set LIST=%LIST% #
:$0

Here are some usage examples:

>>> sum.bat 1 0
             19 bytes

>>> sum.bat 3 6
              9 bytes

>>> sum.bat 2
ERROR: to few arguments!

>>> sum.bat 1 0
             19 bytes

>>> sum.bat 13 4
ERROR: unexpected argument!

I have to admit that I tested this approach in Windows command prompt with command extensions disabled but not in a real MS-DOS environment.

aschipfl
  • 33,626
  • 12
  • 54
  • 99
  • I don't think `lang-dos` is a known language the code prettifier supports. In any case its not properly highlighting your code, so I think you'd be better with `lang-none`. – Ross Ridge Jan 16 '19 at 21:42
  • @RossRidge, `lang-dos` or `lang-cmd` seems to recognise a few commands; but the main reason why I use it is the highlighting of "strings" and //remarks, which I find quite convenient... – aschipfl Jan 17 '19 at 09:26
  • 1
    @RossRidge `lang-dos` is indeed unsupported, which will make it use the default highlighter [Is there syntax highlighting for DOS command line operations or DOS batch files?](https://meta.stackexchange.com/q/207277/230282). `lang-vb` or sometimes `lang-bash` is a better choice for batch files – phuclv Jan 17 '19 at 14:02