System.CommandLine

Seems the string reported from System.CommandLine is different from Mac and Windows.
A quick test show this:

Mac:

/Users/massimo/Desktop/My Application.debug.app/Contents/MacOS/My Application.debug a b c

Windows:

"C:\\Users\\massimo\\Desktop\\Xojo 2015r2.1 Remote Debugger\\Temp\\DebugMy Application\\DebugMy Application.exe" a b c

On Windows, the first argument (the launched application) is quoted, while on Mac is not. I can handle this, sure, but the problem is on Mac I can’t split arguments properly, when the path contains spaces. Now I wish on Mac to have the quoted application path like on Windows, also for consistency.

The LR reports
“…if the application “Foo.exe” was executed with the call “Foo.exe a b c”, then CommandLine would contain “Foo.exe a b c”. This is valid for Windows, Mac OS X built using the Mach-O format, and Linux applications”

Which is not what happens, so the LR is wrong or a bug exists.

So far, the only solution I found is quoting the arguments, to distinguish them from the application path.
Any suggestion on how to solve the blank space with unquoted path on Mac?

[quote=192902:@Massimo Valle]Seems the string reported from System.CommandLine is different from Mac and Windows.
A quick test show this:

Mac:

/Users/massimo/Desktop/My Application.debug.app/Contents/MacOS/My Application.debug a b c

Windows:

"C:\\Users\\massimo\\Desktop\\Xojo 2015r2.1 Remote Debugger\\Temp\\DebugMy Application\\DebugMy Application.exe" a b c

On Windows, the first argument (the launched application) is quoted, while on Mac is not. I can handle this, sure, but the problem is on Mac I can’t split arguments properly, when the path contains spaces. Now I wish on Mac to have the quoted application path like on Windows, also for consistency.

The LR reports
“…if the application “Foo.exe” was executed with the call “Foo.exe a b c”, then CommandLine would contain “Foo.exe a b c”. This is valid for Windows, Mac OS X built using the Mach-O format, and Linux applications”

Which is not what happens, so the LR is wrong or a bug exists.

So far, the only solution I found is quoting the arguments, to distinguish them from the application path.
Any suggestion on how to solve the blank space with unquoted path on Mac?[/quote]

Mach-O is Mac OS-9, Classic and before. I doubt you need that. Mac OS X being based on Linux definitely needs space to be escaped.

I would guess URLPath to be consistent on all platforms. Then you can split that, and finally use DecodeURLComponent to get the name of the folder in clear with spaces.

Hope it is not too lame for you. Maybe there is a declare for that :wink:

Michel, thanks for your answer first.

Something to correct:
Mach-O is OS X only and coming from NeXTStep/OpenStep. Mac OS 9/Classic was PEF :wink:
Also OS X is not based on Linux but on BSD Unix, though they are both POSIX compliant.

Now, about your suggestion, I don’t understand how to use it, sorry.
The System.CommandLine provide me a string with the executable path as the first argument. Quoted on Windows and unquoted and unescaped on Mac. How I can sort the path from the argument when the SPACE is used as a separator for the arguments and a real space on the path as well?

[quote=192918:@Massimo Valle]Michel, thanks for your answer first.

Something to correct:
Mach-O is OS X only and coming from NeXTStep/OpenStep. Mac OS 9/Classic was PEF :wink:
Also OS X is not based on Linux but on BSD Unix, though they are both POSIX compliant.

Now, about your suggestion, I don’t understand how to use it, sorry.
The System.CommandLine provide me a string with the executable path as the first argument. Quoted on Windows and unquoted and unescaped on Mac. How I can sort the path from the argument when the SPACE is used as a separator for the arguments and a real space on the path as well?[/quote]

OK. Macho is OSX, MacOSX is Cocoa. And sure, Unix.

FWIW, here is a possible solution :

[code]Sub Action()
dim totalshebam as string = system.commandline

dim appname as string = app.ExecutableFile.nativePath

dim arguments as string = Replace(totalshebam,appname,"")
arguments = ReplaceAll(arguments,chr(34),"") // For PC
arguments = ReplaceAll(arguments,"/","") // For Mac

system.DebugLog arguments

End Sub
[/code]

Tested on Mac only:

  dim myPath as string = App.ExecutableFile.NativePath
  dim pattern as string = """?\\Q" + myPath.ReplaceAllB( "\\E", "\\\\EE\\Q" ) + "\\E""? (.*)"
  
  dim rx as new RegEx
  rx.SearchPattern = pattern
  dim match as RegExMatch = rx.Search( System.CommandLine )
  dim options as string
  if match IsA RegExMatch then
    options = match.SubExpressionString( 1 )
  end if

Or just use @Jeremy Cowgar 's excellent OptionParser:

https://github.com/jcowgar/xojo-option-parser

Er, I mean, use that after I fix it for the desktop. :slight_smile:

Ug, can’t be fixed on the desktop without access to the raw command line. Now I have to look for a declare or something.

Thanks both Michel and Kem. Both solutions works.
I feel using RegEx for this is maybe like shooting with a cannon, so I will probably adopt the Michel solution.

Sorry to correct you again, but Mach-O has nothing to do with Cocoa or UNIX.
Mach-O is the binary executable format used formerly in NeXTStep and now on OS X
Like PEF (Preferred Executable Format) was for Mac OS Classic.
Linux uses ELF

[quote=192956:@Massimo Valle]Sorry to correct you again, but Mach-O has nothing to do with Cocoa or UNIX.
Mach-O is the binary executable format used formerly in NeXTStep and now on OS X
Like PEF (Preferred Executable Format) was for Mac OS Classic.
Linux uses ELF[/quote]

The LR is not quite clear on that one. So Mac-O is the format of what I call (probably inappropriately) the Unix executable in /Contents/MacOS/ , right ? And that applies to Carbon as well as Cocoa, right ? Could even be a Console program, right ?

Then, in the LR, it is said for TargetMacOS [quote]Used to indicate that you are compiling code for OS X using the Cocoa framework.[/quote] I thought that was TargetCocoa … Lions and tigers and bears! Oh my :wink:

I will have one of these day to test each Targetsomething and try to make heads and tails of all these.

Regex does work very nicely, though. Even if I do not quite understand the magic :wink:

[quote=192960:@Michel Bujardet]The LR is not quite clear on that one. So Mac-O is the format of what I call (probably inappropriately) the Unix executable in /Contents/MacOS/ , right ? And that applies to Carbon as well as Cocoa, right ? Could even be a Console program, right ?

Then, in the LR, it is said for TargetMacOS I thought that was TargetCocoa … Lions and tigers and bears! Oh my :wink:

I will have one of these day to test each Targetsomething and try to make heads and tails of all these.[/quote]

The fact is these constants simply stacked up in time.
TargetMachO was introduced when it was possible to create Mac OS Classic executable (PEF) and Mac OS X introduced the new format Mach-O. This is nowadays useless, since the only executable format on Mac is Mach-O.
Then, a Mach-O executable (or library) can be Carbon or Cocoa and/or intel 32 / 64 bit and/or PowerPC 32/64 bit.
Theoretically speaking the Mach-O format can contain multiple executable in various flavours, so you can have a superFAT application containing Carbon, Cocoa, 32bit, 64bit, ARM, PPC or even a linux executable all in one big file.

Try running otool on the terminal like the example below to see the Mach header of a library or executable file:

otool -hv /Applications/TextEdit.app/Contents/MacOS/TextEdit

Doing this on a FAT file you will see all the executable listed.

I should admit that having started back with System 7, I never quite tried to understand what was going on with all the motions we had to go through, from 68000 to PPC, from Classic to Mac OS X based on Unix, then from PPC to Intel. In the meantime, font support went from Font/Da Mover (yuk), to suitcases and Fonts folder, then all that switched to TTF while I had users completely lost. The worst was when Lion dropped Rosetta and people lost entire font collections as the suitcase format stopped being supported.

I bow to the splendid support RB provided in the troubled times of FAT binary, when it was very difficult to know which processor a program would end up running under… We complain about Yosemite…

Thank you for the clarification.

On the Mac, this will work too and is probably the “proper” way to do it (conceptually, I mean, as the code itself may have flaws):

 Shared Function CommandLineArgs() As String()
  // Return an array of command-line arguments
  
  dim result() as string
  
  #if TargetMacOS and not DebugBuild then
    const kCocoaLib = "Cocoa.framework"
    
    declare function NSClassFromString lib kCocoaLib (aClassName as CFStringRef) as Ptr
    declare function defaultCenter lib kCocoaLib selector "processInfo" (class_id as Ptr) as Ptr
    declare function arguments lib kCocoaLib selector "arguments" (obj_id as Ptr) as Ptr
    declare function m_count lib kCocoaLib selector "count" (obj as Ptr) as UInteger
    declare function objectAtIndex lib kCocoaLib selector "objectAtIndex:" (theArray as Ptr, idx as Integer) as CFStringRef
    
    static c as Ptr = defaultCenter(NSClassFromString("NSProcessInfo"))
    dim nsArrayRef as Ptr = arguments(c)
    dim ub as integer = m_count(nsArrayRef) - 1
    for i as integer = 0 to ub
      dim s as string = objectAtIndex(nsArrayRef, i)
      result.Append s
    next
    
  #else
    
    result = ParseStringValue(System.CommandLine) // Not perfect
    
  #endif
  
  return result
End Function

I’ll post the code behind ParseStringValue too if you need it.

Thank you Kem.

Unfortunatelly I need to do it cross platform.
The best solution indeed would be Xojo providing this in a consistent way. Like the first argument (the launched app) quoted in both Mac and Windows.

Btw, not even the Michel solution and the RegEx one are perfect, since they rely on the absolute path, while the app could be launched from terminal with a relative path.

Go get @Jeremy Cowgar 's OptionParser. I just updated the code with cross-platform declares so you can pull the array as OptionParser.CommandLineArgs in a Desktop app and it will report it consistently across platforms.

(Thanks @Thomas Tempelmann for the Linux and Windows code.)

https://github.com/jcowgar/xojo-option-parser

Be sure to get the develop branch.

Oh, and it will use my custom code in the Debug Build to emulate the parsing you’d find in the built app.

And the OptionParser class will not just give you access to Args properly now, but will also greatly simplify your processing of those arguments and provide features/functionality for your users that would take a bit of time to roll on your own.

P.S. Thanks Kem for taking that task on and submitting back to OptionParser, that’s the power of Open Source at work!

Thanks Kem and Jeremy. This ended to be the best solution :smiley:

[quote=192939:@Kem Tekinay]Or just use @Jeremy Cowgar 's excellent OptionParser:

https://github.com/jcowgar/xojo-option-parser [/quote]

I agree on using the option parser… it works very well.