[Pointless-Fight] Shell Arguments/Parameters

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

[Pointless-Fight] Shell Arguments/Parameters

Post by Octacone »

It has been a long time (like 30 days) since my last interaction with Basic OS. Now, I am struggling with the console arguments. I want to be able to do stuff like "echo hello", etc. Now I was thinking about getting it to work with some string split methods, but my string split is screwed up. Should I port a random string split or there is another way?
Last edited by Octacone on Sun Oct 30, 2016 6:01 am, edited 1 time in total.
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
User avatar
SpyderTL
Member
Member
Posts: 1074
Joined: Sun Sep 19, 2010 10:05 pm

Re: Shell Arguments/Parameters

Post by SpyderTL »

http://forum.osdev.org/viewtopic.php?f=13&t=28654

Here is a discussion that I started 2 years ago (wow...) about my idea for parsing console commands. It may give you some ideas about where to start if you want a fairly simple yet powerful command line parser that supports nested commands, like

Code: Select all

openFile("myFile.txt").writeText("Add this text to the bottom of the file.").getFileLength()
This approach has worked well for my needs, so just let me know if the link makes sense to you, or if you have any questions.

Good luck.
Project: OZone
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott
User avatar
Ch4ozz
Member
Member
Posts: 170
Joined: Mon Jul 18, 2016 2:46 pm
Libera.chat IRC: esi

Re: [Pointless-Fight] Shell Arguments/Parameters

Post by Ch4ozz »

Well, to give you a simple answer atleast (please dont just copy and paste the code) here is the one I coded for SIMPLE parsing.
I coded it a way so all the data can fit into one page and only one malloc is needed.
This has been done because I do this in kernel rather then usermode.

Code: Select all

char **cmd_to_argv(char *commandline, int *_argc)
{
	if(!commandline || !_argc)
		return 0;
	
	int argc = 1;
	bool b_escaped = true;
	char **argv = malloc_p(4096, PTE_WRITE);
	
	argv[0] = commandline;
	
	while(*commandline && argc < 1024)
	{
		if(*commandline == '"' || *commandline == 39)
		{
			b_escaped = !b_escaped;
			*commandline = '\0';

			if(!b_escaped)
			{
				if(*(commandline + 1) != '"' && *(commandline + 1) != 39 && *(commandline + 1) != '\0')
				{
					argv[argc] = commandline + 1;
					argc++;
				}
			}
		}
		else if(*commandline == ' ' && b_escaped)
		{
			*commandline = '\0';

			if(*(commandline + 1) != '"' && *(commandline + 1) != 39 && *(commandline + 1) != ' ' && *(commandline + 1) != '\0')
			{
				argv[argc] = commandline + 1;
				argc++;
			}
		}

		commandline++;
	}
	
	*_argc = argc;
	
	return argv;
}
User avatar
crunch
Member
Member
Posts: 81
Joined: Wed Aug 31, 2016 9:53 pm
Libera.chat IRC: crunch
Location: San Diego, CA

Re: [Pointless-Fight] Shell Arguments/Parameters

Post by crunch »

Inspired by Ch4ozz's post (and my own need for a halfway decent non-strtok parser), I cooked something up and figured I would share it. It should fit most general use cases, as you can specify the delimiter and escaped character strings. It relies heavily on the use of strspn() but that should be trivial to implement.
Escaped characters are replaced with '\0' if there is a match (i.e. for quotations), otherwise they are ignored.
Multiple delimiters are likewise ignored (i.e. multiple spaces, tabs, etc). Hopefully someone can find this useful.

example usage as follows:

Code: Select all

char** argv = tokenize("echo \"hello world\"", " ", "\"", &argc);

Code: Select all

/* Released to public domain */
/* Simple argument parsing - returns an array of char* pointers
@str is the string to be parsed
@delim is a string containing characters to split by
@escape is a string containing characters to escape ("\"\'\n", etc) - optional
@_argc is a pointer to integer containing the size of returned pointer array */

char** tokenize(const char* str, const char* delim, const char* escape, int* _argc) {
	if (!str || !_argc || !delim)
		return NULL;
	int i, argc = 0;
	char* split = strdup(str);

	/* first pass, approximate how many arguments there are for allocation */
	while(*str) {
		if (strspn(str, delim)) {
			argc++;
			str += strspn(str, delim);
		}
		str++;
	}

	/* allocate a pointer array of the proper size */
	char** ret = malloc(sizeof(char*) * argc);
	argc = 0;
	ret[argc++] = split;

	/* second pass, split strings by the delimiter */
	while(*split) {
		if (escape && strspn(split, escape)) {
			i = strspn(split, escape);
			/* save the escaped character for matching */
			char c = *split;
			
			split += i;
			/* if we can't find a matching character to escape, ignore it */
			if (!strchr(split, c)) {
				ret[argc++] = split - i;
				split++;
				continue;
			}
			*(split - i) = '\0';
			ret[argc++] = split;
			/* pointer to first matching character */
			split = strchr(split, c);
			*split = '\0';
			split++;
			/* Make sure we don't return the last character as an argument */
			if (!strspn(split, delim) && (strlen(split) > 1)) 
				ret[argc++] = split;	
				
		}
		
		if (strspn(split, delim)) {
			i = strspn(split, delim);
			*split = '\0';
			split += i;
			/* if there's an escaped character, try to escape it */
			if (escape && strspn(split, escape))
				continue;
			ret[argc++] = split;
		}
		else
			split++;
	}
	*_argc = argc;
	return ret;
}
Post Reply