April 29, 2024, 10:34:59 AM

News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!


Main Subroutine

Started by Zen, December 11, 2005, 09:37:29 AM

Previous topic - Next topic

0 Members and 8 Guests are viewing this topic.

Zen

Just a little idea. Could it be possible for the main subroutine take an optional string peramater. So if you run your program with some command arguments you can get them straight from the peramater?

like this...



global sub main(string args) {

if (args = "/gui") {

/* Run GUI Mode */

}

}



Its only an idea but i think it would be quite usefull.

Lewis

Parker

In C you do it like this:
int main(int argc, char *argv[])
{
    for (int i = 1; i < argc; i++)
    {
        printf("%s\n",argv[i]);
    }
}

which would print all the command line arguments one by one, except for your program's name (argv[0]). So it might look something like this:

-input
somefile.txt
-output
otherfile.lst

assuming you called it like
myapp.exe -input "somefile.txt" -output "otherfile.lst"

Also, I believe in Aurora the DECLARE statement dictates what are the parameters and return value, although this is generated for you, but if Paul automatically declared main() to have an integer and string array parameter, it wouldn't matter whether or not you had them in your definition, you could use them.

Zen

Ohh Damn. I thought i had a new idea, ohh well i dont want it any more then!!! lol

I do think it would be a good idea though as it would save you having to declare all the imports etc.

Lewis

Ionic Wind Support Team

Actually we are going to differ from C a bit here.

Writing a couple of startup functions to return the number of params passed and a string array to access them.  Just so I don't have to make main a CDECL function.

Example:

count  = GetArgCount();
lastarg = GetArgString(count - 1);




Ionic Wind Support Team

Ionic Wind Support Team

Quote from: Lewis on December 11, 2005, 09:50:10 AM
Ohh Damn. I thought i had a new idea, ohh well i dont want it any more then!!! lol

I do think it would be a good idea though as it would save you having to declare all the imports etc.

Lewis

imported functions are not runtime elements.  Meaning the declare statement is processed at compile time.  Nothing your program does could change that.  Unless you were using LoadLibrary and dynamically linking to functions.

Ionic Wind Support Team

John S

Paul,   I'm trying to pass command line arguments using the functions you discussed in this thread, but it won't compile.  I have tested it without arguments and with fixed values and it works.  Please help me with this simple program.

Code:

// second_one.src
// this is my second program

declare GetArgCount();
declare GetArgString(i as int);

global sub main ()
{
def n, i, count as int;

count = GetArgCount();
i = GetArgString(count - 1);

cls();
writeln ("this is my second program\n\n");
writeln ("i equals "+str$(i)+"\n\n");

for ( n = 0; n < i; n++ )
{
   writeln (str$(n)+"\n");
}
while (GetKey() = "");

return;
}
John Siino, Advanced Engineering Services and Software

Mike Stefanik

I don't think those functions are implemented yet, he was just giving an example of what they could look like. You could pretty easily create your own using the GetCommandLine API function. For example:


declare import,GetCommandLine alias "GetCommandLineA" (),string;

global sub GetArgCount(),int
{
ÂÃ,  ÂÃ,  def strCommandLine as string;
ÂÃ,  ÂÃ,  def nIndex as int;
ÂÃ,  ÂÃ,  def nSpaces as int;
ÂÃ,  ÂÃ,  def nQuoted as int;
ÂÃ,  ÂÃ,  def nLength as int;
ÂÃ,  ÂÃ,  def nCount as int;

ÂÃ,  ÂÃ,  strCommandLine = GetCommandLine();
ÂÃ,  ÂÃ,  nIndex = 0;
ÂÃ,  ÂÃ,  nSpaces = 1;
ÂÃ,  ÂÃ,  nQuoted = 0;
ÂÃ,  ÂÃ,  nLength = 0;
ÂÃ,  ÂÃ,  nCount = 0;

ÂÃ,  ÂÃ,  while (strCommandLine[nIndex] <> 0)
ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  if (strCommandLine[nIndex] = 0x20 and nQuoted = 0)
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  nSpaces++;
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  if (nSpaces = 1)
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  nCount++;
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  nLength = 0;
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  }
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  }
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  else
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  nSpaces = 0;
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  if (strCommandLine[nIndex] = 0x22)
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  if (nQuoted = 0)
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  nQuoted = 1;
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  }
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  else
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  nQuoted = 0;
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  }
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  }
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  nLength++;
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  }
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  nIndex++;
ÂÃ,  ÂÃ,  }

ÂÃ,  ÂÃ,  if (nLength > 0)
ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  nCount++;
ÂÃ,  ÂÃ,  }

ÂÃ,  ÂÃ,  return nCount;
}

global sub GetArgString(nArgument as int),string
{
ÂÃ,  ÂÃ,  def strCommandLine as string;
ÂÃ,  ÂÃ,  def nIndex as int;
ÂÃ,  ÂÃ,  def nSpaces as int;
ÂÃ,  ÂÃ,  def nQuoted as int;
ÂÃ,  ÂÃ,  def strArgument as string;
ÂÃ,  ÂÃ,  def nCount as int;

ÂÃ,  ÂÃ,  strCommandLine = GetCommandLine();
ÂÃ,  ÂÃ,  nIndex = 0;
ÂÃ,  ÂÃ,  nSpaces = 1;
ÂÃ,  ÂÃ,  nQuoted = 0;
ÂÃ,  ÂÃ,  strArgument = "";
ÂÃ,  ÂÃ,  nCount = 0;

ÂÃ,  ÂÃ,  while (strCommandLine[nIndex] <> 0)
ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  if (strCommandLine[nIndex] = 0x20 and nQuoted = 0)
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  nSpaces++;
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  if (nSpaces = 1)
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  nCount++;
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  if (nCount = nArgument + 1)
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  return strArgument;
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  }
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  strArgument = "";
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  }
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  }
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  else
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  nSpaces = 0;
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  if (strCommandLine[nIndex] = 0x22)
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  if (nQuoted = 0)
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  nQuoted = 1;
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  }
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  else
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  nQuoted = 0;
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  }
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  }
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  strArgument += chr$(strCommandLine[nIndex]);
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  }
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  nIndex++;
ÂÃ,  ÂÃ,  }

ÂÃ,  ÂÃ,  if (nCount <> nArgument)
ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  strArgument = "";
ÂÃ,  ÂÃ,  }

ÂÃ,  ÂÃ,  return strArgument;
}

global sub main()
{
ÂÃ,  ÂÃ,  def nArgCount as int;
ÂÃ,  ÂÃ,  def nArgument as int;
ÂÃ,  ÂÃ,  def strArgument as string;

ÂÃ,  ÂÃ,  nArgCount = GetArgCount();
ÂÃ,  ÂÃ, 
ÂÃ,  ÂÃ,  writeln("The number of arguments: " + str$(nArgCount) + "\n");

ÂÃ,  ÂÃ,  for (nArgument = 0; nArgument < nArgCount; nArgument++)
ÂÃ,  ÂÃ,  {
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  strArgument = GetArgString(nArgument);
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  writeln("Argument " + str$(nArgument) + ": " + strArgument + "\n");
ÂÃ,  ÂÃ,  }

ÂÃ,  ÂÃ,  return;
}


Note that this implementation is fairly basic and isn't terribly efficient because it's walking through the entire command line string each time. Not a huge deal since you're generally not talking about a large string anyway, but it would be more efficient to just parse it once in the startup code, etc.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

Zen

If i was you i would wait for ArgV and ArgC. I tried to make one before for another project i am working on, i thought i was prety good actually untill parker checked it for me.... Well it just got picked to pieces. ;)

Lewis

Parker

A commandline argument can be one of two things:
- A quoted string, that can contain anything
- A piece of a string surrounded by whitespace
So a commandline like "C:\My Program\MyProgram.exe" -x -t /Lib/Dir
would be parsed into the four tokens - the filename, the switches beginning with -, and the /Lib/Dir option. Another thing to keep in mind is that command line arguments can be quite large, so using a simple STRING won't work. I use a pointer and call GetCommandLine twice to get the size and allocate a string large enough.

John S

Thanks guys,
I was trying the "GetArgCount() & GetArgString()" stuff in every way and combination I could think of and couldn't get it to work.  I thought it was operator error.  I don't need this right now, I was just trying to cut my teeth on something different.  I'm making fast progress re-reading my C++ resources and am finding that Aurora does resemble C++ in command/function format.
John Siino, Advanced Engineering Services and Software

Zen

Some things are like C++ (so ive been told) and some things are like C but Aurora seems a lot less complex. I think Paul will be doing the ArgV and ArgC functions soon. I dont know if he will be adding them as seperate functions or as peramaters to the main subroutine like in C/C++

Lewis

John S

I thing the parameter/argument implementation should include the following options:

1.  " - "   as in  "someprogram.exe -a_first_argument  -a_second_argument ... "
2.  " / "   as in  "someprogram.exe /a_first_argument  /a_second_argument  ... "
3.  "  "   as in  "someprogram.exe a_single_argument_that_might_include_spaces_or_other_delimiters"

The first two options are common applications and should be the default.  The third would allow the programmer to inport the argument(s) as a character array and parse it to his/her hearts content.

Perhaps just implementing the third (3) option is the fastest and easiest and leave the rest to the programmer.

Mike, I will look at you snippet tonight and try to understand/apply it, thanks.
John Siino, Advanced Engineering Services and Software

Rod

Generally speaking, I think that whether the arguments on the command line begin with "-", "/" or nothing depends on the individual programmer and the design of his/her program. The command-line functions we are waiting for would simply return the strings, and let us do with them as we will.

Hopefully, Paul's implementation will handle quoted arguments, so that a line like -x -y -x "aurora rocks" would return four arguments, not five.

As an add-on package (class?), one of us could implement something similar to the "getopts" commands that exist for Unix shell scripts and Perl scripts. IF enough people are interested in using them.

Mike Stefanik

Quote from: Parker on January 13, 2006, 08:16:30 AM
Another thing to keep in mind is that command line arguments can be quite large, so using a simple STRING won't work. I use a pointer and call GetCommandLine twice to get the size and allocate a string large enough.

Is there an inherent limitation on the length of a string using the "string" type? I'd have thought that'd be done away with.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

Zen

Yes there is a limit. the string type can only hold text upto 255 characters long. However dstring is only limited to the amount of available memory

Lewis

John S

I thought that there was a limit on commandline argument length governed by the OS's environmental space (256 bytes or something) unless explicitly changed.  I think I remember something like that from the old 8 & 16 bit DOS days (Stone Age).

I don't know if I ever saw a 255 character command line.  Is that realistic?  I guess with the modern directory and file names being what they are it may well be that a dstring type would be necessary.
John Siino, Advanced Engineering Services and Software

Parker

But that's not all correct in itself. The dstring type is given a specific size at compile time and is not really a great idea. The implementation I'm working on (should be done later today) has these lines:
string *__cl_text;
...
__cl_text = new(byte, len(GetCommandLine())+1);
*__cl_text = GetCommandLine();

yeah, you have to call the function twice, but at least there will be no crashes or huge unused memory.

Mike Stefanik

Why allocate string space for it? Just declare GetCommandLine to return a pointer, and then assign it to a byte pointer. In other words:


declare import,GetCommandLine alias "GetCommandLineA" (),pointer;
...
def *lpCommandLine as byte;

lpCommandLine = GetCommandLine();


You could then use *lpCommandLine[nIndex] to walk through the string up to the terminating null-byte. The only reason I could see in allocating space for it would be if you wanted to modify the string, and even then you wouldn't need to actually call GetCommandLine more than once; just store the return value in a pointer at module scope.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

Parker

Even if it works, I prefer to not do that way since it looks a little risky to me. I don't know, maybe it isn't, it just looks like it.
And a string pointer is actually the same as a byte pointer.

Mike Stefanik

It isn't riskier than anything else pointer-related. Here's a version of my previous code that has no limitation on the length of the command line (for convenience I still use the string type for individual arguments, but that's easily changed as well if you really think you're going to have a single argument > 255 characters):


declare import,GetCommandLine alias "GetCommandLineA" (),pointer;
byte *__lpCmdLine = null;
int __nCmdArgs = 0;

global sub GetArgCount(),int
{
int nIndex = 0;
int nSpaces = 1;
int nQuoted = 0;
int nLength = 0;
int nCount = 0;

if (__nCmdArgs > 0)
return __nCmdArgs;

if (__lpCmdLine = null)
__lpCmdLine = GetCommandLine();

while (*__lpCmdLine[nIndex] <> 0)
{
if (*__lpCmdLine[nIndex] = 0x20 and nQuoted = 0)
{
nSpaces++;
if (nSpaces = 1)
{
nCount++;
nLength = 0;
}
}
else
{
nSpaces = 0;
if (*__lpCmdLine[nIndex] = 0x22)
{
if (nQuoted = 0) nQuoted = 1 else nQuoted = 0;
}
nLength++;
}
nIndex++;
}

if (nLength > 0)
{
nCount++;
}

return nCount;
}

global sub GetArgString(nArgument as int),string
{
int nIndex = 0;
int nSpaces = 1;
int nQuoted = 0;
int nCount = 0;
string strArgument = "";

if (__lpCmdLine = null)
__lpCmdLine = GetCommandLine();

while (*__lpCmdLine[nIndex] <> 0)
{
if (*__lpCmdLine[nIndex] = 0x20 and nQuoted = 0)
{
nSpaces++;
if (nSpaces = 1)
{
nCount++;
if (nCount = nArgument + 1)
return strArgument;

strArgument = "";
}
}
else
{
nSpaces = 0;
if (*__lpCmdLine[nIndex] = 0x22)
{
if (nQuoted = 0) nQuoted = 1 else nQuoted = 0;
}
strArgument += Chr$(*__lpCmdLine[nIndex]);
}
nIndex++;
}

if (nCount <> nArgument)
{
strArgument = "";
}

return strArgument;
}

global sub main()
{
int nArgCount;
int nArgument;
string strArgument;

nArgCount = GetArgCount();

writeln("The number of arguments: " + str$(nArgCount) + "\n");

for (nArgument = 0; nArgument < nArgCount; nArgument++)
{
strArgument = GetArgString(nArgument);
writeln("Argument " + str$(nArgument) + ": " + strArgument + "\n");
}

return;
}


It's also somewhat "optimized" in that GetCommandLine is only called once, and GetArgCount can be multiple times without the overhead of walking through the command line string each time.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

Mike Stefanik

January 13, 2006, 05:34:52 PM #20 Last Edit: January 13, 2006, 05:37:27 PM by Mike Stefanik
Quote from: Parker on January 13, 2006, 05:15:43 PM
And a string pointer is actually the same as a byte pointer.

I don't know what Paul envisions in terms of internationalization, but that's actually a very dangerous assumption to make. A "string" in an internationalized application would be Unicode (for example, a BSTR) in which case it each character is going to be at least 16-bits, possibly as much as 32-bits.

It's safe to cast a return value from GetCommandLineA as a byte pointer because it's explicitly the ANSI version of the function, and in Windows the command line is always passed as an ANSI null-terminated string. But as a general rule, assumptions about characters being the same size as bytes can get you into trouble.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

Parker

Yes, but if you're using the "A" version of the function, it's safe to assume you're dealing with ASCII strings in which case the string type will be the same as a byte array.

sapero

here's IE6 GetCommandLineToArgW translated to ansi version - returns arguments count and an array of string pointers.
Command line is twice parsed - at first time size of all arguments is calculated (+ dword alignment) then heap is allocated, on top is writen array of pointers, then c.m.l. agruments

struct this_heap // the argv pointer
{
string *argument1; // argc times
string *argument2;
...
byte firstarg[sizeof firstarg];
byte second ...[];
}



extern void system(...);


// example usage - compile for console
global sub main()
{
int argc;
string *argv;

if CommandLineToArgA(&argc, &argv) // false if out of memory
{
writeln(using("arguments count: #\n",argc));

// print arguments using argv directly
for (int a=0; a<argc; a++)
{
writeln(using("#: &\n", a, *(pointer)argv[a]));
}
FreeHeap(argv); // free memory
}
system("pause");
return;
}




/////////////////////////////////////
extern pointer GetCommandLineA();
extern int lstrcpyn(string *dest, string *src, int max);
//
// creates an array of strings and string pointers
//
// argc - pointer to integer variable that on output
//     returns number of returned strings
//
// argv - pointer to variable that on output receives
//     memory pointer

global sub CommandLineToArgA(int *argc, string *argv),int
{
*argc = 0;
*argv = 0;
// calculate number of parameters and memory size
int     argcnt=0;
pointer first;
int     length;
int     memsize=0;

// string *cmd = GetCommandLineA();
string *cmd = "\"my program.exe\" arg1 arg2 arg 3 \"arg 4\" \"c:\\program files\\myexe.exe\""; // demo
string *arg = cmd;

while GetArgument(&arg, &first, &length)
{
argcnt++;
length++; // add null
while (length&3) {length++;}
memsize+=(4+length);// pointer + aligned string
}

// allocate memory for our array
pointer *pmem = AllocHeap(memsize);     // pointer array
if (pmem=null) return false;
string *pmstring = pmem + (argcnt<<2); // strings

// restore command line pointer
arg = cmd;

for (int i=0; i<argcnt; i++)
{
*pmem[i] = pmstring;
GetArgument(&arg, &first, &length);
lstrcpyn(pmstring, first, length+1);
pmstring+=(length+1); // next aligned string
while (pmstring&3) {pmstring++;}
}

*(int)argc = argcnt;
*argv = pmem;

return true;
}

//
// 'lpcmdl' [in, out] - pointer to string-pointer variable
//     on input must points to any location in parsed string
//     on output points to next argument
//
// 'first' [optional, out] - pointer to string-pointer variable
//     on output points to first character to argument
//
// 'length' [optional, out] - pointer to integer variable
//     on output returns the size of string pointed by 'first'
//
// return values:
//   true :  all ok
//   false: no more parameters
//
sub GetArgument(pointer *lpcmdl, pointer *first, int *length),int
{
byte separator, bCur;
// skip spaces
while (*(byte)*lpcmdl=32) {*(int)lpcmdl++;}

separator = *(byte)*lpcmdl;
if (separator=0) // end of string
{
if (first<>null) *(int)first=0;
if (length<>null) *(int)length=0;
return false;
}

// arguments separator can be quotation or space
if (separator = "\"")
{
*(int)lpcmdl++; // skip first quotation
}
else
{
separator=" "; // fix separator
}

if (first<>null) *(int)first = *(int)lpcmdl; // store beginning of argument
bCur = *(byte)*lpcmdl;

// find next space or quotation
while (bCur<>separator) and (bCur<>0)
{
*(int)lpcmdl++;
bCur = *(byte)*lpcmdl;
}

if (length<>null)
{
*(int)length = *(int)lpcmdl-*(int)first;
}

if (bCur="\"") *(int)lpcmdl++;
return true;
}