//----------------------------------------------------------------------------// // GNU GPL OS/K // // // // Desc: Command line parsing utilities // // // // // // 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 . // //----------------------------------------------------------------------------// #include #include #include // // 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 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] // // TODO long escape sequences // get program command line if cmdLine == NULL // error_t KalCmdLineToArgVecEx(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 = *argv + 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 KalCmdLineToArgVec(const char *cmdLine, int *argcPtr, char *bufptr) { return KalCmdLineToArgVecEx(cmdLine, argcPtr, bufptr, false); }