191 lines
6.3 KiB
C
191 lines
6.3 KiB
C
//----------------------------------------------------------------------------//
|
|
// GNU GPL OS/K //
|
|
// //
|
|
// Desc: Command line parsing utilities //
|
|
// //
|
|
// //
|
|
// Copyright © 2018-2020 The OS/K Team //
|
|
// //
|
|
// This file is part of OS/K. //
|
|
// //
|
|
// OS/K is free software: you can redistribute it and/or modify //
|
|
// it under the terms of the GNU General Public License as published by //
|
|
// the Free Software Foundation, either version 3 of the License, or //
|
|
// any later version. //
|
|
// //
|
|
// OS/K is distributed in the hope that it will be useful, //
|
|
// but WITHOUT ANY WARRANTY//without even the implied warranty of //
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
|
// GNU General Public License for more details. //
|
|
// //
|
|
// You should have received a copy of the GNU General Public License //
|
|
// along with OS/K. If not, see <https://www.gnu.org/licenses/>. //
|
|
//----------------------------------------------------------------------------//
|
|
|
|
#include <sh/argv.h>
|
|
#include <ex/malloc.h>
|
|
|
|
//
|
|
// Converts command line to an argument vector
|
|
//
|
|
// This function assumes that *argv is a (ARG_MAX * 2)-wide buffer,
|
|
// whose second half will be filled with strings in succession;
|
|
// with the address of the nth string stored in the first half,
|
|
// specifically at argv[n-1]
|
|
//
|
|
error_t ShCmdLineToArgVecEx(const char *cmdLine,
|
|
int *argcPtr,
|
|
char *bufptr,
|
|
bool doEscaping)
|
|
{
|
|
int argc = 0;
|
|
char quotes = 0;
|
|
bool started = false;
|
|
bool escaping = false;
|
|
size_t written = 0;
|
|
error_t retcode = EOK;
|
|
bool maxreached = false;
|
|
|
|
// An ARG_MAX-wide buffer
|
|
char **argv = (char **)bufptr;
|
|
|
|
assert(argv && cmdLine);
|
|
|
|
// Another ARG_MAX-wide buffer
|
|
char *buffer = bufptr + ARG_MAX;
|
|
|
|
argv[0] = buffer;
|
|
|
|
// Null-terminate current argv slot
|
|
// and save the start of next string
|
|
// Macro'd to avoid copypasting code
|
|
#define NULLTERM_AND_SAVE \
|
|
*buffer = 0; \
|
|
argv[++argc] = ++buffer; \
|
|
written += sizeof(char *) + 1; \
|
|
|
|
// Is character a blank character?
|
|
// To be replaced by ctype stuff once
|
|
// that's implemented
|
|
#define ISBLANK(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
|
|
|
|
// Both " and ' are valid quotes chars
|
|
// XXX aren't there more?
|
|
#define ISQUOTE(c) ((c) == '\'' || (c) == '"')
|
|
|
|
|
|
// Go through the command line
|
|
for (; *cmdLine; cmdLine++) {
|
|
|
|
// Make sure we don't go beyond ARG_MAX bytes
|
|
maxreached = written >= ARG_MAX - 1;
|
|
maxreached = maxreached || argc >= (int)(ARG_MAX/sizeof(char *) - 1);
|
|
|
|
if (maxreached) {
|
|
// Sanity check
|
|
assert(written == ARG_MAX - 1);
|
|
|
|
// All we have left is one byte for the null-terminator
|
|
// (or argv maxed out)
|
|
*buffer = 0;
|
|
|
|
// Did we write anything in this slot?
|
|
if (started) {
|
|
argc++;
|
|
}
|
|
|
|
// We're done, get out of here
|
|
retcode = ENOMEM;
|
|
break;
|
|
}
|
|
|
|
// Switch to next argv slot
|
|
if (ISBLANK(*cmdLine) && !quotes && !escaping) {
|
|
|
|
// Has slot even started?
|
|
if (started) {
|
|
started = false;
|
|
NULLTERM_AND_SAVE;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// Escaping next character
|
|
if (*cmdLine == '\\' && !escaping && doEscaping) {
|
|
escaping = true;
|
|
continue;
|
|
}
|
|
|
|
// Deal with escape sequences
|
|
if (escaping) {
|
|
if (*cmdLine == 'n') *buffer++ = '\n';
|
|
else if (*cmdLine == 'r') *buffer++ = '\r';
|
|
else if (*cmdLine == 't') *buffer++ = '\t';
|
|
else if (*cmdLine == 'f') *buffer++ = '\f';
|
|
else if (*cmdLine == 'v') *buffer++ = '\v';
|
|
|
|
written++;
|
|
started = true;
|
|
escaping = false;
|
|
continue;
|
|
}
|
|
|
|
// Deal with quotes
|
|
if (ISQUOTE(*cmdLine) && !escaping) {
|
|
|
|
// Quoted text always fills a whole slot
|
|
// Note that this is the only way an empty
|
|
// string can be put in a slot
|
|
if (!quotes && !started) {
|
|
quotes = *cmdLine;
|
|
started = true;
|
|
continue;
|
|
}
|
|
|
|
// End a quote block
|
|
if (quotes == *cmdLine && ISBLANK(cmdLine[1])) {
|
|
quotes = 0;
|
|
started = false;
|
|
NULLTERM_AND_SAVE;
|
|
continue;
|
|
}
|
|
|
|
// Quotes were either preceeded by unquoted non-blank text
|
|
// or couldn't close quoted text block because succeeded
|
|
// by text; we consider this " to be escaped and fall through
|
|
}
|
|
|
|
// Just a regular character, or it is being escaped
|
|
written++;
|
|
started = true;
|
|
escaping = false;
|
|
*buffer++ = *cmdLine;
|
|
}
|
|
|
|
// Ensures that the last string is null-terminated
|
|
*buffer = 0;
|
|
|
|
// Ensures that argv[argc] == NULL
|
|
argv[++argc] = NULL;
|
|
|
|
// Update *argcPtr, but we don't mind if
|
|
// NULL was passed for argcPtr
|
|
if (argcPtr) {
|
|
*argcPtr = argc;
|
|
}
|
|
|
|
return retcode;
|
|
}
|
|
|
|
#undef ISQUOTE
|
|
#undef ISBLANK
|
|
#undef NULLTERM_AND_SAVE
|
|
|
|
error_t ShCmdLineToArgVec(const char *cmdLine,
|
|
int *argcPtr,
|
|
char *bufptr)
|
|
{
|
|
return ShCmdLineToArgVecEx(cmdLine, argcPtr, bufptr, false);
|
|
}
|