Parsing Command Line Switches

Share your advanced PureBasic knowledge/code with the community.
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Parsing Command Line Switches

Post by BackupUser »

Code updated For 5.20+

Restored from previous forum. Originally posted by ebs.

A number of my programs accept command line parameters of the form /r, /d10, /o=C:\Windows, or "C:\Program Files". Note that embedded spaces are OK if the parameter is enclosed in quotes, like the last example.

Here is a routine to parse command line parameters of the form /x or /x= (the equals sign is ignored, so these are the same), and one parameter of the form "this could be a path with spaces in it". If there is more than one parameter without a switch, only the first one will be returned.

It has only one procedure: CommandLineParameter(Switch.s). The first time it is called, it parses all command line parameters and stores them in a linked list. It then checks the linked list to see if there is a parameter for the specified switch letter. Parameters are returned as a string; use Val() if you need a numeric value. Note that switches are not case-sensitive; /r and /R are the same.

Try out this example using a command line like: /r=abc /sdef /t=123 "this is a file".

Code: Select all

Declare.s CommandLineParameter(Switch.s)

Structure CmdLineParam
  Switch.s
  Parameter.s
EndStructure

NewList CmdLineParams.CmdLineParam()

Debug CommandLineParameter("r")
Debug CommandLineParameter("s")
Debug Val(CommandLineParameter("t"))
Debug CommandLineParameter("")

; return command line parameter for specified switch letter
Procedure.s CommandLineParameter(Switch.s)
  Shared CmdLineParsed.l
  Shared CmdLineParams()
  ; parse command line on first invocation
  If CmdLineParsed = #False
    Repeat
      ; get next parameter
      Param.s = ProgramParameter()
      If Len(Param)
        AddElement(CmdLineParams())
        If Left(Param, 1) = "/"
          ; add switch to element
          CmdLineParams()\Switch = UCase(Mid(Param, 2, 1))
          ; add parameter to element (ignore "=" if present)
          Parameter.s = Mid(RemoveString(Param, "="), 3, 255)
          If Len(Parameter)
            CmdLineParams()\Parameter = Parameter
          Else
            ; make parameter a space if switch only
            CmdLineParams()\Parameter = " "
          EndIf
        Else
          ; one parameter without switch allowed (can contain spaces if in quotes)
          CmdLineParams()\Switch = ""
          CmdLineParams()\Parameter = Param
        EndIf
      EndIf
    Until Len(Param) = 0
    CmdLineParsed = #True
  EndIf
  
  ; search for specified command line switch letter
  ResetList(CmdLineParams())
  UCSwitch.s = UCase(Switch)
  While NextElement(CmdLineParams())
    If CmdLineParams()\Switch = UCSwitch
      ; switch found - return parameter
      ProcedureReturn CmdLineParams()\Parameter
    EndIf
  Wend
  ; switch not found
  ProcedureReturn ""
EndProcedure

BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by Max..

Ebs,

one problem with this routine using PB's ProgramParameter() function - you cannot return quotes itself, which limits sometimes (for example I had to store enclosing quotes for associating file endings with programs, like word.exe "%1".

Wouldn't it be better to use GetCommandLine_ instead?

Max.

AMD Athlon, Matrox G550, Win 2K SP3, IE 6.0, PB 3.62
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by ebs.

Max,

I agree - if you need to be able to get quotes, GetCommandLine_ is better. I've never needed to do that, in fact, I usually wind up having to remove any quote characters manually. For the majority of my needs, this was a quick and easy solution.

Regards,
Eric

Originally posted by Max.

Ebs,

one problem with this routine using PB's ProgramParameter() function - you cannot return quotes itself, which limits sometimes (for example I had to store enclosing quotes for associating file endings with programs, like word.exe "%1".

Wouldn't it be better to use GetCommandLine_ instead?

Max.

AMD Athlon, Matrox G550, Win 2K SP3, IE 6.0, PB 3.62
User avatar
Tenaja
Addict
Addict
Posts: 1949
Joined: Tue Nov 09, 2010 10:15 pm

Re: Parsing Command Line Switches

Post by Tenaja »

This is perfect for my current project, but for one thing...neither this one nor the one Mistrel has shared ( http://purebasic.fr/english/viewtopic.php?f=12&t=44123 ) one has the provision for passing a filename containing spaces. (If you wrap it in quotes, the quote must be the first character.) By any chance has anybody already worked out a solution for that?
User avatar
Tenaja
Addict
Addict
Posts: 1949
Joined: Tue Nov 09, 2010 10:15 pm

Re: Parsing Command Line Switches

Post by Tenaja »

Tenaja wrote: By any chance has anybody already worked out a solution for that?
Yes, here it is. :D

For commands including a space, such as a filename, use a command-line like this:
//r "this is a file.txt" //a "This is a second file.txt"
Note the double slashes, and the space after the switch (r and a in this example).

Here is the updated code:

Code: Select all

; http://purebasic.fr/english/viewtopic.php?f=12&t=5731&p=23042

; Here is a routine to parse command line parameters of the form
; 	/x or /x= (the equals sign is ignored, so these are the same), 
; 	and one parameter of the form "this could be a path with spaces in it". 
; 	If there is more than one parameter without a switch, only the first one will be returned.
;		(Subsequent parameters without switches are saved in the list, but never retrieved.)

; It has only one procedure: CommandLineParameter(Switch.s). The first time it is called, 
; it parses all command line parameters and stores them in a linked list. It then checks the 
; linked list to see if there is a parameter for the specified switch letter. Parameters are 
; returned as a string; use Val() if you need a numeric value. Note that switches are not 
; case-sensitive; /r and /R are the same.

; Try out this example using a command line like: /r=abc /sdef /t=123 "this is a file".


; one problem with this routine using PB's ProgramParameter() function-- 
; 	you cannot return quotes itself, which limits sometimes 
; 	(for example I had to store enclosing quotes for associating 
; 	file endings with programs, like word.exe "%1".

;-Update: --------------------------------------
; This Version fixes that by allowing a double //:
;		using a command line like: //r "this is a file" //a "This is a second file"
;		Notice the space after the switch.

Declare.s CommandLineParameter(Switch.s)


	Structure CmdLineParam
		Switch.s
		Parameter.s
	EndStructure
	
	NewList CmdLineParams.CmdLineParam()


; return command line parameter for specified switch letter
Procedure.s CommandLineParameter(Switch.s)
	Shared CmdLineParsed.l
	Shared CmdLineParams()
	; parse command line on first invocation
	If CmdLineParsed = #False
		Repeat
			; get next parameter
			Param.s = ProgramParameter()
			If Len(Param)
				AddElement(CmdLineParams())
				; New, to allow for a switch, followed by a space, then a filename--e.g.: //a "This is a filename with spaces.txt"
				If Left(Param, 2) = "//"		; Provision for a space, then a parameter enclosed in quotes.
					; add switch to element
					CmdLineParams()\Switch = UCase(Mid(Param, 3, 1))
					; add parameter to element (ignore "=" if present)
					Parameter.s = ProgramParameter()
					If Len(Parameter)
						CmdLineParams()\Parameter = Parameter
					Else
						; make parameter a space if switch only
						CmdLineParams()\Parameter = " "
					EndIf
				ElseIf Left(Param, 1) = "/"
					; add switch to element
					CmdLineParams()\Switch = UCase(Mid(Param, 2, 1))
					; add parameter to element (ignore "=" if present)
					Parameter.s = Mid(RemoveString(Param, "="), 3, 255)
					If Len(Parameter)
						CmdLineParams()\Parameter = Parameter
					Else
						; make parameter a space if switch only
						CmdLineParams()\Parameter = " "
					EndIf
				Else
					; one parameter without switch allowed (can contain spaces if in quotes)
					CmdLineParams()\Switch = ""
					CmdLineParams()\Parameter = Param
				EndIf
			EndIf
		Until Len(Param) = 0
		CmdLineParsed = #True
	EndIf
	
	; search for specified command line switch letter
	ResetList(CmdLineParams())
	UCSwitch.s = UCase(Switch)
	While NextElement(CmdLineParams())
		If CmdLineParams()\Switch = UCSwitch
			; switch found - return parameter
			ProcedureReturn CmdLineParams()\Parameter
		EndIf
	Wend
	; switch not found
	ProcedureReturn ""
EndProcedure
Post Reply