I wanted to try to write something without using HL sytanx
below is a short section of code that parses the command line. Any feedback of how horrible it is, or general pointers please. It is working code, i tried to make code readable and as short as possible since it is not time critical
I need the location of 2nd C and the length to the last t:
"C:\app.exe" "C:\test.txt"
This is the command line when test.txt is dragged onto app.exe (edit: WRONG)
so i came up with
LOCAL FileNameBegin:DWORD
LOCAL FileNameLength:DWORD
;------------------------------------------------------------------------------------------------
; Section - Parse Command Line
; At end of section FileNameBegin will be a pointer to the begining of the filename
; and FileNameLength will contain it's length. eax,ecx,edx are trashed
; ecx = counter for quotes
; eax = pointer to current character in string
; edx = current character
;------------------------------------------------------------------------------------------------
invoke GetCommandLine
xor ecx,ecx
mov FileNameLength,ecx
jmp pcBegin
pcError:
print "Command line not valid. Fully qualified name in quotes is required. ",13,10
ret
pcTick:
inc ecx
cmp ecx,4
jne @f
sub eax,FileNameBegin
mov FileNameLength,eax
JMP pcEnd
@@:
inc eax ; Advance pointer
cmp ecx, 3
jne pcBegin
mov FileNameBegin,eax
pcBegin:
movzx edx,byte ptr [eax] ; edx == char pointed to by eax
test edx,edx
jz pcError ; check for null character
cmp edx,'"'
je pcTick ; check for quote
inc eax ; Advance pointer
jmp pcBegin
pcEnd:
; end Section
;------------------------------------------------------------------------------------------------
edit:
Above is incorrect, the quotes are only there if a space is involved, and i was testing from desktop. Basically code is pointless on it's own. But code does work if both test.txt and app.exe are on desktop on XP. Advice on it's structure is more important to me than it's usefulness.
Suppose you are given a null terminated string that contains two quoted sections and you want the location of the second without the quotes and it's length:
"String One is not that important" "But i need this one please!"
edit again:
:) i suppose instead of comparing to 4 and than comparing to 3 i could check if less than 3 than check if equal to 4.
or start at 4 and count down to zero might make it simpler
Hi,
"String One is not that important" "But i need this one please!"
include \masm32\include\masm32rt.inc
.data
string BYTE 34,"String One is not that important",34,32,34,"But i need this one please!",34,0
l DWORD 0
startpos DWORD 0
.code
start:
push ebx ; save ebx
xor ebx,ebx ; how many quotes did we encounter?
xor ecx,ecx ; position in string
xor edx,edx ; position of start of second string
@@:
mov edx,[offset string+ecx*1]
and edx,0FFh
inc ecx
cmp dl,34 ; is it a quote?
jne @B ; not a quote
inc ebx
cmp ebx,3 ; is it the 3rd quote? If so we are at the second string
je SOS
jmp @B
SOS:
sub ecx,1
push ecx ; store the start position
add ecx,1
@@:
mov edx,[offset string+ecx*1]
and edx,0FFh
inc ecx
cmp dl,0 ; are we at the end yet?
je EOS
jne @B
EOS:
sub ecx,3 ; remove the length of the null, the last quote, and the fact we are +1 after the end of the string
pop edx
sub ecx,edx ; subtract edx (start pos) from ecx (end pos) to get the length. Result in ecx
mov l,ecx
mov startpos,edx
print "The length of the string is "
print str$(l),13,10
print "The start of the second string is at character "
print str$(startpos),13,10
pop ebx ; restore ebx
ret
end start
Tested.
Best regards,
Robin.
Using the library functions GetCL and szLen you could solve it like this:
INCLUDE \masm32\include\masm32rt.inc
.CODE
start:
print chr$(13,10)
call ParseCommandLine
cmp eax, TRUE
je _okay
_not_ok:
print "failure"
invoke ExitProcess,1
_okay:
print "okay"
invoke ExitProcess,0
; -------------------------------------------------------------------------
; ParseCommandLine - PROC
;
; IN: ----
; OUT: eax == FALSE no parameter specified
; eax == TRUE valid parameter
; or if you like
; mov eax, FileNameBegin to return the address of the parameter
; -------------------------------------------------------------------------
ParseCommandLine PROC uses ecx edx
LOCAL szPara1[MAX_PATH] :BYTE ; value of parameter1
LOCAL FileNameBegin :DWORD ; pointer to parameter1
LOCAL FileNameLength :DWORD
; -------------------------------------------------------------------------
; first, lets check if any parameter is given
; -------------------------------------------------------------------------
invoke GetCL, 1, ADDR szPara1 ; get 1st parameter
cmp eax, 1 ; eax == 1 if 1st parameter exists
jne _nopara1
; -------------------------------------------------------------------------
; Does parameter begin with a quote?
; -------------------------------------------------------------------------
;
; no need to check this, because GETCL strips the quotes
; if only one quote is given it will return FALSE
; -------------------------------------------------------------------------
; if parameter given, get its length and get its pointer (FileNameBegin)
; -------------------------------------------------------------------------
lea eax,szPara1
mov FileNameBegin, eax
invoke szLen, ADDR szPara1
mov FileNameLength, eax
print "Parameter: "
print FileNameBegin, " Length: "
print str$(FileNameLength), " - Address: "
print hex$(FileNameBegin),13,10
mov eax, TRUE ; alternatively: mov eax, FileNameBegin
; to return address of FileName to caller
jmp _exit
_wrongpara1:
print "Command line not valid. Fully qualified name in quotes is required. ",13,10
mov eax, FALSE
jmp _exit
_nopara1:
print "No parameter specified.", 13,10
mov eax,FALSE
_exit:
ret
ParseCommandLine ENDP
end start
Joe,
Usually the trick to do this type of work where you are working with double quotes is to have 2 loops, the first scans for the opening quote, if it finds it in branches to a second loop that scans for the closing quote. If the second loop find a zero before the closing quote you exit on an error. With a bit more effort you can do the same thing from the first loop with single quotes then branch to a different loop that handles the single closing quote. In either loop that you branch to you can if you want write the contents between the quotes to a seperate memory buffer for later use.
This technique gtives you alternate quotes so you can have "This is 'a' test" or 'This is "a" test'.
So maybe more like this :)
include \masm32\include\masm32rt.inc
NextQuote MACRO
LOCAL startloop,outloop
startloop:
movzx edx,byte ptr [eax] ; edx = char pointed to by eax
inc eax ; advance pointer
cmp edx,22h
je outloop ; jump out if "
test edx,edx
jnz startloop ; jump start if not null
xor eax,eax ; make eax null if char null
outloop:
ENDM
.data
TheString db 22h,"String One is not that important",22h,20h,22h,"But i need this one please!",22h,0
.code
start:
call main
inkey
exit
main proc
LOCAL quoted:DWORD
LOCAL command:LPSTR
LOCAL pStart:DWORD
LOCAL nLength:DWORD
mov eax,OFFSET TheString
NextQuote
test eax,eax
jz Error
NextQuote
test eax,eax
jz Error
NextQuote
test eax,eax
jz Error
mov pStart,eax
NextQuote
test eax,eax
jz Error
sub eax,pStart
dec eax
mov nLength,eax
print pStart
print str$(nLength)
ret
Error:
print "String malformed",13,10
ret
main endp
end start
delete
You could use CommandLineToArgvW as well if you don't mind unicode - see http://www.masm32.com/board/index.php?topic=11477.msg86259#msg86259
Joe,
Have a play with this one. Its a test algo that seems to be working properly.
IF 0 ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Build this template with "CONSOLE ASSEMBLE AND LINK"
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include\masm32rt.inc
get_quoted PROTO :DWORD,:DWORD,:DWORD
.data
txt1 db 34,"Test 1",34,"xxx",34,"Test 2",34,"yyy",34,"Test 3",34,"zzz",34,"Test 4",34,"???",0
txt2 db 64 dup (0)
.code
start:
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
call main
exit
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
main proc
LOCAL cloc :DWORD ; current location pointer
mov cloc, 0 ; start at offset 0 in source
lbl0:
mov cloc, rv(get_quoted,ADDR txt1,ADDR txt2,cloc)
.if cloc == 0
print "finished",13,10
jmp lbl1
.else
print OFFSET txt2,13,10
.endif
inkey
jmp lbl0
lbl1:
ret
main endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
get_quoted proc src:DWORD,dst:DWORD,cloc:DWORD
push esi
push edi
xor edx, edx ; zero flag
mov esi, src
mov edi, dst
add esi, cloc ; add current offset to ESI
mov ecx, esi ; store start in ECX
sub esi, 1
scanloop:
add esi, 1
movzx eax, BYTE PTR [esi]
test eax, eax
jz theend
cmp eax, 34 ; test for double quote
je dqlp
jmp scanloop
dqlp:
mov edx, 1 ; use EDX as flag
mov BYTE PTR [edi], al ; write the 1st quote
add edi, 1
@@:
add esi, 1
movzx eax, BYTE PTR [esi]
mov [edi], al
add edi, 1
test eax, eax
jz dqerr
cmp al, 34
jne @B
jmp outlp
sqlp:
outlp:
mov BYTE PTR [edi], 0 ; terminate buffer
test edx, edx
jz theend
sub esi, ecx
add cloc, esi
mov eax, cloc
add eax, 1
jmp quit
dqerr:
mov eax, -1
jmp quit
theend:
xor eax, eax
quit:
pop edi
pop esi
ret
get_quoted endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end start
well i went ahead and wrote a CommandLineToArgvA. The only two links i could find for a similar function were:
http://www2.alter.org.ua/en/docs/win/args/ which does a strange calculation for size. I am not sure if i understand it but does not seem correct.
and
lost url - which allocated memory during the loop for each argument
It its only version 1... It needs to be completely rewriten. It does not preserve registers and i want to go about it differently. But does everything MS CommandLineToArgvW does other than escape characters which i do not desire.
I also may only compare to space for whitespace. i also did /r /n and /t because that first link at the top did.
include \masm32\include\masm32rt.inc
.code
start:
call main
inkey
exit
CommandLineToArgvA proc lpCmdLine:DWORD, pNumArgs:DWORD
xor ecx,ecx ; Character Needed Counter
xor edx,edx ; Boolean state of in a word
xor esi,esi ; Number of Arguments
mov ebx,lpCmdLine ; Pointer to string
jmp MainLoop3
QuoteLoop1:
inc esi
xor edx,edx
inc ebx
push ebx
jmp @f
QuoteLoop2:
inc ebx
@@:
movzx eax, byte ptr [ebx]
cmp eax, 22h
je @f
inc ecx
jmp QuoteLoop2
@@:
push ebx
jmp MainLoop2
MainLoop1:
inc ecx
MainLoop2:
inc ebx
MainLoop3:
movzx eax, byte ptr [ebx]
cmp eax, 22h
je QuoteLoop1
test edx,edx
jz MainLoop4
test eax,eax
jz MainLoop5
cmp eax, 20h
je @F
cmp eax, 09h
je @F
cmp eax, 0Dh
je @F
cmp eax, 0Ah
je @F
jmp MainLoop1
@@:
xor edx,edx
push ebx
jmp MainLoop2
MainLoop4:
test eax,eax
jz MainLoop6
cmp eax, 20h
je MainLoop2
cmp eax, 09h
je MainLoop2
cmp eax, 0Dh
je MainLoop2
cmp eax, 0Ah
je MainLoop2
inc esi
inc edx
push ebx
jmp MainLoop1
MainLoop5:
push ebx
MainLoop6:
mov eax,pNumArgs
mov [eax],esi
mov eax,5 ; sizeof pointer + null terminate
imul eax,esi ; foreach argument
add ecx, eax ; added to size
cmp ecx,0
jg @f
xor eax,eax
ret ; negative or zero size of memory
@@:
push ecx
push 0
call LocalAlloc
test eax,eax
jnz @f
ret ; Problem allocating memory
@@:
; eax = new memory
; ebx = data pointer in new memory
; ecx
; edx
mov ebx,esi ; the amount of arguments
shl ebx,2 ; multiplied by 4
add ebx,eax ; plus the new memory location
;preferably in the mainloop need to push
;an extra dword for the destination and
;push the size instead of the end address
;and it will not require poping just
;modifying the destination address and calling
mov eax,ebx ; make new memory pointer same as data pointer and work backwards
DataLoop1:
pop edx ; End Address
pop ecx ; Start Address
sub edx,ecx ; Size of
sub eax,4 ; write index
mov [eax],ebx
push esi
invoke MemCopy,ecx,ebx,edx
pop esi
dec esi
add ebx,edx
inc ebx
mov [ebx],byte ptr 0;null terminate
inc ebx
test esi,esi
jz @f
jmp DataLoop1
@@:
ret
CommandLineToArgvA endp
main proc
LOCAL argc:DWORD
LOCAL ppargv:DWORD
invoke GetCommandLine
lea ecx,argc
invoke CommandLineToArgvA,eax,ecx
mov ppargv,eax
print "Arguments : "
print str$(argc),13,10
mov ecx,0
mov eax,ppargv
.while ecx != argc
pushad
print str$(ecx)
print " : "
popad
pushad
print [eax+ecx*4],13,10
print " ",13,10
popad
inc ecx
.endw
ret
main endp
end start