2019-01-21 15:00:04 +01:00
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
// GNU GPL OS/K //
|
|
|
|
// //
|
|
|
|
// Desc: Command line parsing utilities //
|
2019-02-16 23:36:33 +01:00
|
|
|
// //
|
|
|
|
// //
|
|
|
|
// Copyright © 2018-2019 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/>. //
|
2019-01-21 15:00:04 +01:00
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
|
2019-05-13 23:22:27 +02:00
|
|
|
#include <ex/argv.h>
|
|
|
|
#include <ex/malloc.h>
|
2019-02-06 14:07:38 +01:00
|
|
|
|
|
|
|
//
|
|
|
|
// Computes argument count, the least N such
|
|
|
|
// that argv[N] == NULL
|
|
|
|
//
|
|
|
|
int KalComputeArgCount(const char **argv)
|
|
|
|
{
|
|
|
|
int argc = 0;
|
|
|
|
|
|
|
|
while (argv[argc]) argc++;
|
|
|
|
|
|
|
|
return argc;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Computes the total size of argv, including
|
|
|
|
// the null-terminators
|
|
|
|
//
|
|
|
|
size_t KalComputeArgVecSize(const char *argv[])
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
for (len = 0; *argv; len += strlen(*argv) + 1, argv++);
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Converts command line to an argument vector
|
|
|
|
//
|
|
|
|
// This function assumes that argv[0] is a pointer
|
|
|
|
// to a ARG_MAX-wide buffer, which will be filled
|
|
|
|
// with strings in succession; the address of the nth
|
|
|
|
// string will be stored in argv[n-1]
|
|
|
|
//
|
|
|
|
// Technically ARG_MAX is the maximum number of bytes
|
|
|
|
// in both the buffer *and* argv, i.e. (argc + 1) * sizeof(char *)
|
|
|
|
// bytes are reserved for the argv[i] pointers, so in fact less than
|
|
|
|
// ARG_MAX bytes are available
|
|
|
|
//
|
|
|
|
// That available space, however, remains strictly higher than 4KB,
|
2019-03-08 09:00:55 +01:00
|
|
|
// which is the POSIX minimum; the current ARG_MAX guarantees 16KB available
|
2019-02-06 14:07:38 +01:00
|
|
|
//
|
|
|
|
// TODO long escape sequences
|
|
|
|
// get program command line if cmdLine == NULL
|
|
|
|
//
|
|
|
|
error_t KalCmdLineToArgVecEx(const char *cmdLine,
|
|
|
|
int *argcPtr,
|
|
|
|
char **argv,
|
|
|
|
bool doEscaping)
|
|
|
|
{
|
|
|
|
int argc;
|
|
|
|
char quotes = 0;
|
|
|
|
bool started = false;
|
|
|
|
bool escaping = false;
|
|
|
|
size_t written = 0;
|
|
|
|
error_t retcode = EOK;
|
|
|
|
|
2019-05-13 23:22:27 +02:00
|
|
|
assert_always(argv && *argv && cmdLine);
|
2019-02-06 14:07:38 +01:00
|
|
|
|
|
|
|
// An ARG_MAX-wide buffer
|
|
|
|
char *buffer = *argv;
|
|
|
|
|
|
|
|
// 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++ + 1; \
|
|
|
|
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 (argc = 0; *cmdLine; cmdLine++) {
|
|
|
|
|
|
|
|
// Make sure we don't go beyond ARG_MAX bytes
|
|
|
|
if (written >= ARG_MAX - (1 + sizeof(char *))) {
|
2019-03-08 09:00:55 +01:00
|
|
|
// Sanity check
|
2019-05-13 23:22:27 +02:00
|
|
|
assert(written == ARG_MAX - (1 + sizeof(char *)));
|
2019-02-06 14:07:38 +01:00
|
|
|
|
|
|
|
// All we have left is one byte for the null-terminator of the current slot
|
|
|
|
// and sizeof(char *) bytes for the NULL at the end of argv
|
|
|
|
*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 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 KalCmdLineToArgVec(const char *cmdLine,
|
|
|
|
int *argcPtr,
|
|
|
|
char **argv)
|
|
|
|
{
|
|
|
|
return KalCmdLineToArgVecEx(cmdLine, argcPtr, argv, false);
|
|
|
|
}
|