os-k/kaleid/extras/argv.c

203 lines
5.7 KiB
C
Raw Normal View History

2019-01-21 15:00:04 +01:00
//----------------------------------------------------------------------------//
// GNU GPL OS/K //
// //
// Authors: spectral` //
// NeoX //
// //
// Desc: Command line parsing utilities //
//----------------------------------------------------------------------------//
#include <kalbase.h>
#include <extras/prog.h>
#include <extras/argv.h>
2019-02-06 14:07:38 +01:00
#include <extras/malloc.h>
//
// 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,
// which is the POSIX minimum; in case of doubt, safely use 4KB
//
// 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;
KalAlwaysAssert(argv && *argv && cmdLine);
// 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 *))) {
// 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);
}