Escape all special characters in printf() - c++

Is there an easy way to escape all special characters in the printf() function?
The reason why I would like to know how to do this is because I am printing a number of characters which may include special characters such as the null character (\0) and the beep character and I just want to see the contents of the string.
Currently I am using the following code
It works for null characters. What would be the easiest way to escape all special characters?
int length;
char* data = GetData( length ); // Fills the length as reference
for( int i = 0; i < length; i++ )
{
char c = data[ i ];
printf( "%c", ( c == 0 ? '\\0' : data[ i ] ) );
}

First of all, '\\0' is a two-character literal, which should really be a two-character string. As for printing all special characters as escape code, you need some more code:
switch (data[i])
{
case '\0':
printf("\\0");
break;
case '\n':
printf("\\n");
break;
/* Etc. */
default:
/* Now comes the "hard" part, because not all characters here
* are actually printable
*/
if (isprint(data[i]))
printf("%c", data[i]); /* Printable character, print it as usual */
else
printf("\\x%02x", data[i]); /* Non-printable character, print as hex value */
break;
}

Use the isprint library function to determine if the character is printable:
#include <ctype.h>
...
if (isprint(data[i]))
printf(" %c", data[i]); // prints character
else
printf(" %d", data[i]); // prints code value for character

In case code needs to write with no ambiguity, using C syntax:
#include <ctype.h>
#include <string.h>
#include <stdio.h>
void EscapePrint(int ch) {
// Delete or adjust these 2 arrays per code's goals
// All simple-escape-sequence C11 6.4.4.4
static const char *escapev = "\a\b\t\n\v\f\r\"\'\?\\";
static const char *escapec = "abtnvfr\"\'\?\\";
char *p = strchr(escapev, ch);
if (p && *p) {
printf("\\%c", escapec[p - escapev]);
} else if (isprint(ch)) {
fputc(ch, stdout);
} else {
// Use octal as hex is problematic reading back
printf("\\%03o", ch);
}
}
void EscapePrints(const char *data, int length) {
while (length-- > 0) {
EscapePrint((unsigned char) *data++);
}
}
Alternatively, code could
void EscapePrint(char sch) {
int ch = (unsigned char) sch;
...
}
void EscapePrints(const char *data, int length) {
while (length-- > 0) {
EscapePrint(*data++);
}
}
To use a hexadecimal-escape-sequence or a shorten octal-escape-sequence, code needs to insure that the next character does not create ambiguity. That complication does not occur in the above code as it uses 3-digit octal-escape-sequences. Amended code would be something like:
} else {
if ((ch == 0) && (nextch < '0' || nextch > '7')) {
fputs("\\0", stdout);
}
else if (!isxdigit((unsigned char) nextch)) {
printf("\\x%X", ch);
}
else {
// Use octal as hex is problematic reading back
printf("\\%03o", ch);
}
}

#include <stdio.h>
#include <ctype.h>
/* Converts a buffer of specified lenth to
* ASCII representation as it was a C string literal.
* Returns how much bytes from source was processed
* (ideally ret == src_sz)
*/
int binbuf_to_escaped_C_literal(const char *src_buf, size_t src_sz, char *dst_str, size_t dst_sz)
{
const char *src = src_buf;
char *dst = dst_str;
while (src < src_buf + src_sz)
{
if (*src == '\\')
{
*dst++ = '\\';
*dst++ = *src++;
}
else if (isprint(*src))
{
*dst++ = *src++;
}
else
{
switch(*src)
{
case '\n':
*dst++ = '\\';
*dst++ = 'n';
break;
case '\r':
*dst++ = '\\';
*dst++ = 'r';
break;
case '\t':
*dst++ = '\\';
*dst++ = 't';
break;
case '\0':
*dst++ = '\\';
*dst++ = '0';
break;
default:
sprintf(dst, "0x%x", *src);
dst += 4;
}
src++;
}
// next iteration requires up to 5 chars in dst buffer, for ex. "0xab\0"
if (dst > (dst_str + dst_sz - 5)) {
break;
}
}
*dst = '\0';
return src - src_buf;
}
int main(int argc, char **argv)
{
const char binbuf[] = "strange \n\r\t\0\0\0\0\0\\\\ string";
size_t sz = sizeof(binbuf) - 1; // drop trailing nul terminator
char escaped[128];
if (binbuf_to_escaped_C_literal(binbuf, sz, escaped, sizeof(escaped)) != sz) {
fprintf(stderr, "Destination string buffer is too small\n");
return 1;
}
printf("Escaped: %s\n", escaped);
// $ ./escape-binary-buf //
// Escaped: strange \n\r\t\0\0\0\0\0\\\\ string //
return 0;
}

Related

C++ sscanf seams not to working as intended

I'm currently working on project for school with an Arduino Uno and came across an issue with C sscanf function. The code bellow is parsing a char array sent over the serial interface to the Arduino. Each data block is separated with a blankspace. I also checked similar question on Stackoverflow but found nothing helpful.
char testInput[] = "t 1 3 65 1";
int registerIndex;
int locoAddr;
int locoSpeed;
int locoDirection;
if(sscanf(testInput, "t %d %d %d %d", &registerIndex, &locoAddr, &locoSpeed, &locoDirection) != 4) {
return;
}
// print for demo:
Serial.print(registerIndex);
Serial.print(": ");
Serial.print("loco:");
Serial.print(locoAddr);
Serial.print(" speed:");
Serial.print(locoSpeed);
Serial.print(" direction:");
Serial.println(locoDirection);
// expected output: "1: loco:3 speed:65 direction:1"
// output: "-18248: loco:-18248 speed:-18248 direction:-18248"
the sscanf function returns the expected integer 4 but doesn't fill the variables with the values so only the default values are printed.
I'm out of ideas what the issue could be. Maybe someone on the internet has a solution.
Edit 1: For better understanding of the complexity of the sketch this might help to understand the code a bit better:
loop in .ino file calls static Commander::process().
Commander::_readLine(...); get called and reads the data from serial.
some more validation is done but should not matter in this case
Commander::parseCommand(command); get called with the command.
registers->setThrottle(command); gets called when the command starts with a "t". registers is volatile so I'm able to call it from the interupt.
in PackageRegisterList::setThrottle is the sscanf method located.
I usually use this version stolen from the Linux kernel:
int ASCII_vsscanf(const char * buf, const char * fmt, va_list args)
{
const char *str = buf;
char *next;
char digit;
int num = 0;
int qualifier;
int base;
int field_width;
int is_sign = 0;
while(*fmt && *str) {
/* skip any white space in format */
/* white space in format matchs any amount of
* white space, including none, in the input.
*/
if (isspace(*fmt)) {
while (isspace(*fmt))
++fmt;
while (isspace(*str))
++str;
}
/* anything that is not a conversion must match exactly */
if (*fmt != '%' && *fmt) {
if (*fmt++ != *str++)
break;
continue;
}
if (!*fmt)
break;
++fmt;
/* skip this conversion.
* advance both strings to next white space
*/
if (*fmt == '*') {
while (!isspace(*fmt) && *fmt)
fmt++;
while (!isspace(*str) && *str)
str++;
continue;
}
/* get field width */
field_width = -1;
if (isdigit(*fmt))
field_width = skip_atoi(&fmt);
/* get conversion qualifier */
qualifier = -1;
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
*fmt == 'Z' || *fmt == 'z') {
qualifier = *fmt++;
if (unlikely(qualifier == *fmt)) {
if (qualifier == 'h') {
qualifier = 'H';
fmt++;
} else if (qualifier == 'l') {
qualifier = 'L';
fmt++;
}
}
}
base = 10;
is_sign = 0;
if (!*fmt || !*str)
break;
switch(*fmt++) {
case 'c':
{
char *s = (char *) va_arg(args,char*);
if (field_width == -1)
field_width = 1;
do {
*s++ = *str++;
} while (--field_width > 0 && *str);
num++;
}
continue;
case 's':
{
char *s = (char *) va_arg(args, char *);
if(field_width == -1)
field_width = INT_MAX;
/* first, skip leading white space in buffer */
while (isspace(*str))
str++;
/* now copy until next white space */
while (*str && !isspace(*str) && field_width--) {
*s++ = *str++;
}
*s = '\0';
num++;
}
continue;
case 'n':
/* return number of characters read so far */
{
int *i = (int *)va_arg(args,int*);
*i = str - buf;
}
continue;
case 'o':
base = 8;
break;
case 'x':
case 'X':
base = 16;
break;
case 'i':
base = 0;
case 'd':
is_sign = 1;
case 'u':
break;
case '%':
/* looking for '%' in str */
if (*str++ != '%')
return num;
continue;
default:
/* invalid format; stop here */
return num;
}
/* have some sort of integer conversion.
* first, skip white space in buffer.
*/
while (isspace(*str))
str++;
digit = *str;
if (is_sign && digit == '-')
digit = *(str + 1);
if (!digit
|| (base == 16 && !isxdigit(digit))
|| (base == 10 && !isdigit(digit))
|| (base == 8 && (!isdigit(digit) || digit > '7'))
|| (base == 0 && !isdigit(digit)))
break;
switch(qualifier) {
case 'H': /* that's 'hh' in format */
if (is_sign) {
signed char *s = (signed char *) va_arg(args,signed char *);
*s = (signed char) ASCII_simple_strtol(str,&next,base);
} else {
unsigned char *s = (unsigned char *) va_arg(args, unsigned char *);
*s = (unsigned char) ASCII_simple_strtoul(str, &next, base);
}
break;
case 'h':
if (is_sign) {
short *s = (short *) va_arg(args,short *);
*s = (short) ASCII_simple_strtol(str,&next,base);
} else {
unsigned short *s = (unsigned short *) va_arg(args, unsigned short *);
*s = (unsigned short) ASCII_simple_strtoul(str, &next, base);
}
break;
case 'l':
if (is_sign) {
long *l = (long *) va_arg(args,long *);
*l = ASCII_simple_strtol(str,&next,base);
} else {
unsigned long *l = (unsigned long*) va_arg(args,unsigned long*);
*l = ASCII_simple_strtoul(str,&next,base);
}
break;
case 'L':
if (is_sign) {
long long *l = (long long*) va_arg(args,long long *);
*l = ASCII_simple_strtoll(str,&next,base);
} else {
unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*);
*l = ASCII_simple_strtoull(str,&next,base);
}
break;
case 'Z':
case 'z':
{
size_t *s = (size_t*) va_arg(args,size_t*);
*s = (size_t) ASCII_simple_strtoul(str,&next,base);
}
break;
default:
if (is_sign) {
int *i = (int *) va_arg(args, int*);
*i = (int) ASCII_simple_strtol(str,&next,base);
} else {
unsigned int *i = (unsigned int*) va_arg(args, unsigned int*);
*i = (unsigned int) ASCII_simple_strtoul(str,&next,base);
}
break;
}
num++;
if (!next)
break;
str = next;
}
return num;
}
Limited but small and good enough for me.

i have a program that takes input a string that's called search which is the target and i want to search in the csv file if the "search" is there

#include <stdio.h>
#include <string.h>
void myFgets(char str[], int n);
int main(int argc, char** argv)
{
if (argc < 2)
{
printf("Usage: csv <csv file path>\n");
return 1;
}
else
{
char ch = ' ', search[100], dh = ' ';
int row = 1;
printf("Enter value to search: ");
myFgets(search, 100);
FILE* fileRead = fopen(argv[1], "r");
if (fileRead == NULL)
{
printf("Error opening the file!\n");
return 1;
}
while ((ch = (char)fgetc(fileRead)) != EOF)
{
char str[100];
int i = 0, pos = ftell(fileRead);
while ((dh = (char)fgetc(fileRead)) != ',')
{
str[i] = dh;
i++;
}
fseek(fileRead, pos + 1, SEEK_SET);
if (strstr("\n", str) != NULL)
{
row++;
}
if (strstr(search, str) != NULL)
{
printf("Value was found in row: %d\n", row);
break;
}
}
}
getchar();
return 0;
}
/*
Function will perform the fgets command and also remove the newline
that might be at the end of the string - a known issue with fgets.
input: the buffer to read into, the number of chars to read
*/
void myFgets(char* str, int n)
{
fgets(str, n, stdin);
str[strcspn(str, "\n")] = 0;
}
in line 39 im getting an error but idk why it seems like im doing everything fine
im trying to loop through the rows and split them by the ',' so i could check if search == to it but its not wokring
im using function strstr to compare 2 strings with each other it works fine and all but the only problem is at the dh
i did fseek after the dh so i dont write in the wrong place in the ch loop
You forgot to terminate the string.
while ((dh = (char)fgetc(fileRead)) != ',')
{
str[i] = dh;
i++;
}
str[i] = '\0'; /* add this to terminate the string */
Also it looks like if (strstr(search, str) != NULL) should be if (strstr(str, search) != NULL) to search for the value to search from the contents of the file.

How to convert int type number/letter to char and *char?

I am using LittleFS library and ESP32 on arduino IDE.
I am reading a file using the example readFile function of LittleFS but I am trying to convert it for my needs.
The text written to the file is of this form:
LettersAndNumbersMax30&LettersAndNumbersMax30&00&00&01&01
Seperated by &. 2 text values of max 30 characters and 4 integers.
I want to build:
char *mytest1 containing the first text
char *mytest2 containing the second text
int mytest3 containing the first integer (2digits)
int mytest4 containing the second integer (2digits)
int mytest5 containing the third integer (2digits)
int mytest5 containing the forth integer (2digits)
file.read() returns and integer always. for example 38 for &.
void readFile(fs::FS &fs, const char * path){
Serial.printf("Reading file: %s\r\n", path);
File file = fs.open(path);
if(!file || file.isDirectory()){
Serial.println("- failed to open file for reading");
return;
}
Serial.println("- read from file:");
while(file.available()){
Serial.write(file.read());
}
file.close();
}
Its fairly straightforward. Test each byte read and act accordingly. Code below doesn't handle signs nor negative numbers. It also doesn't check if there are only digits for integers in the file.
#include ....
struct record_t
{
char myState1[31];
char myState2[31];
int myState3;
int myState4;
int myState5;
int myState6;
};
record_t record;
bool readFile(fs::FS &fs, const char * path);
void setup()
{
// ...
}
void loop()
{
//...
if (readFile(/*...*/))
{
Serial.printf("file reads OK\r\n");
//...
}
}
bool readFile(fs::FS &fs, const char * path)
{
Serial.printf("Reading file: %s\r\n", path);
File file = fs.open(path);
if (!file || file.isDirectory())
{
Serial.println("- failed to open file for reading");
return;
}
Serial.println("- read from file:");
int state = 0;
int index = 0;
// clear record.
record.myState1[0] = 0;
record.myState2[0] = 0;
record.myState3 = 0;
record.myState4 = 0;
record.myState5 = 0;
record.myState6 = 0;
bool valid = false;
for (int i = file.read(); i != -1; i = file.read())
{
char c = i & 0xFF;
Serial.write(c); // file.read() returns an int, that's why Serial.write()
// was printing numbers.
switch(state)
{
case 0:
if (index > sizeof(record.myState1) - 1) // avoid buffer overflow
index = sizeof(record.myState1) - 1;
if (c != '&')
{
record.myState1[index++] = c;
}
else
{
record.myState1[index] = 0;
++state;
index = 0;
}
break;
case 1:
if (index > sizeof(record.myState2) - 1) // avoid buffer overflow
index = sizeof(record.myState2) - 1;
if (c != '&')
{
record.myState2[index++] = c;
}
else
{
record.myState2[index] = 0;
++state;
index = 0;
}
break;
case 2:
if (c != '&')
record.myState3 = record.myState3 * 10 + (c - '0');
else
++state;
break;
case 3:
if (c != '&')
record.myState4 = record.myState4 * 10 + (c - '0');
else
++state;
break;
case 4:
if (c != '&')
record.myState5 = record.myState5 * 10 + (c - '0');
else
++state;
break;
case 5:
valid = true;
if (c != '&')
record.myState6 = record.myState6 * 10 + (c - '0');
else
++state;
break;
default: // reaching here is an error condition? You decide.
return false;
}
}
file.close();
if (!valid)
{
// clear record.
record.myState1[0] = 0;
record.myState2[0] = 0;
record.myState3 = 0;
record.myState4 = 0;
record.myState5 = 0;
record.myState6 = 0;
}
return valid;
}
file.read returns integer. So the integer is printed.
You to convert it to the string.
while(file.available()){
char s[2] = {0};
s[0] = file.read();
Serial.write(s);
}

string manipulating in C?

I want to print an array of characters, these characters are underscores first.
Then the user can write characters on these underscores.I used gotoxy() but it doesn't work properly.
That is what i wrote:
int main(void)
{
char arr[20];
int i;
char ch;
clrscr();
for(i=0;i<=20;i++)
{
textattr(0x07);
cprintf("_");
}
do
{
for(i=0;i<=20;i++)
{
//gotoxy(i,0);
//ch = getche();
if( isprint(ch) == 1)
{
arr[i] = ch;
gotoxy(i,0);
//printf("%c",ch);
}
}
} while(i == 20);
getch();
return 0;
}
The first thing is this: You probably don't want to have all those calls to gotoxy, textattr and cprintf in your main function, since that is not what the main function is supposed to do.
It is much more likely that the main function's purpose is "to read some text from the user, presented nicely in an input field". So you should make this a function:
static int
nice_input_field(char *buf, size_t bufsize, int x, int y) {
int i, ch;
gotoxy(x, y);
for (i = 0; i < bufsize - 1; i++) {
cprintf("_");
}
i = 0;
gotoxy(x, y);
while ((ch = readkey()) != EOF) {
switch (ch) {
case '...': /* ... */
break;
case '\b': /* backspace */
cprintf("_");
i--;
gotoxy(x + i, y);
break;
case '\t': /* tabulator */
case '\n': /* enter, return */
buf[i] = '\0';
return 0; /* ok */
default: /* some hopefully printable character */
if (i == bufsize - 1) {
cprintf("\a"); /* beep */
} else {
buf[i++] = ch;
gotoxy(x + i, y);
cprintf("%c", buf[i]);
}
}
}
/* TODO: null-terminate the buffer */
return 0;
}
Printing an array of characters is fairly easy:
char* str = your_array;
while(*str) {
putc(*str++);
}
From memory that should print a string out to the screen.
Your code is very DOS-specific. There is not a good general solution to the problem of reading immediate input in a portable way. It does get asked quite often, so I think the C FAQ broke down and included an answer which you might want to seek out.
That said, I think your bug is that gotoxy(1, 1) is the upper corner of the screen, not 0,0. So you want gotoxy(i, 1)

convert string to argv in c++

I have an std::string containing a command to be executed with execv, what is the best "C++" way to convert it to the "char *argv[]" that is required by the second parameter of execv()?
To clarify:
std::string cmd = "mycommand arg1 arg2";
char *cmd_argv[];
StrToArgv(cmd, cmd_argv); // how do I write this function?
execv(cmd_argv[0], cmd_argv);
Very non-unixy answers here. What's wrong with:
std::string cmd = "echo hello world";
execl("/bin/sh", "/bin/sh", "-c", cmd.c_str(), NULL);
Why bother writing a command line parser when there's a perfectly good one already on the system?
(Note: one good reason is because you don't trust the string you're about to execute. One hopes that this is already true, but the shell will do "more" with that string than a naive whitespace-splitter will and thus open more security holes if you aren't careful.)
std::vector<char *> args;
std::istringstream iss(cmd);
std::string token;
while(iss >> token) {
char *arg = new char[token.size() + 1];
copy(token.begin(), token.end(), arg);
arg[token.size()] = '\0';
args.push_back(arg);
}
args.push_back(0);
// now exec with &args[0], and then:
for(size_t i = 0; i < args.size(); i++)
delete[] args[i];
Of course, this won't work with commans that use quoting like rm "a file.mp3". You can consider the POSIX function wordexp which cares about that and much more.
Perhaps split_winmain from Boost.ProgramOptions. Boost is a good choice in most cases.
http://www.boost.org/doc/libs/1_40_0/doc/html/program_options/howto.html#id1396212
If you are only interested in Windows (other kernels generally don't know about command lines in the Windows sense), you can use the API function CommandLineToArgvW which uses the same conventions as the MS C runtime.
In general it depends on the quoting style of the platform and/or the shell. The Microsoft C Runtime uses a quite different style than e.g. bash!
A combination of the c_str() string method and strtok() to split it up by spaces should get you the array of strings you need to pass to exec() and its related functions.
OK, I've been stumbling over this myself enough times. This is straight "C", so it can be plugged into either C or C++. It treats single and double quote strings differently. The caller is responsible for deallocating argv[0] (if not NULL) and argv.
#include
#include
#include
#include
typedef enum {
STR2AV_OK = 0,
STR2AV_UNBALANCED_QUOTE
} str_to_argv_err_t;
#ifndef NUL
#define NUL '\0'
#endif
static char const nomem[] = "no memory for %d byte allocation\n";
static str_to_argv_err_t
copy_raw_string(char ** dest_p, char ** src_p);
static str_to_argv_err_t
copy_cooked_string(char ** dest_p, char ** src_p);
static inline void *
Xmalloc(size_t sz)
{
void * res = malloc(sz);
if (res == NULL) {
fprintf(stderr, nomem, sz);
exit(EXIT_FAILURE);
}
return res;
}
static inline void *
Xrealloc(void * ptr, size_t sz)
{
void * res = realloc(ptr, sz);
if (res == NULL) {
fprintf(stderr, nomem, sz);
exit(EXIT_FAILURE);
}
return res;
}
str_to_argv_err_t
string_to_argv(char const * str, int * argc_p, char *** argv_p)
{
int argc = 0;
int act = 10;
char ** res = Xmalloc(sizeof(char *) * 10);
char ** argv = res;
char * scan;
char * dest;
str_to_argv_err_t err;
while (isspace((unsigned char)*str)) str++;
str = scan = strdup(str);
for (;;) {
while (isspace((unsigned char)*scan)) scan++;
if (*scan == NUL)
break;
if (++argc >= act) {
act += act / 2;
res = Xrealloc(res, act * sizeof(char *));
argv = res + (argc - 1);
}
*(argv++) = dest = scan;
for (;;) {
char ch = *(scan++);
switch (ch) {
case NUL:
goto done;
case '\\':
if ( (*(dest++) = *(scan++)) == NUL)
goto done;
break;
case '\'':
err = copy_raw_string(&dest, &scan);
if (err != STR2AV_OK)
goto error_leave;
break;
case '"':
err = copy_cooked_string(&dest, &scan);
if (err != STR2AV_OK)
goto error_leave;
break;
case ' ':
case '\t':
case '\n':
case '\f':
case '\r':
case '\v':
case '\b':
goto token_done;
default:
*(dest++) = ch;
}
}
token_done:
*dest = NUL;
}
done:
*argv_p = res;
*argc_p = argc;
*argv = NULL;
if (argc == 0)
free((void *)str);
return STR2AV_OK;
error_leave:
free(res);
free((void *)str);
return err;
}
static str_to_argv_err_t
copy_raw_string(char ** dest_p, char ** src_p)
{
for (;;) {
char ch = *((*src_p)++);
switch (ch) {
case NUL: return STR2AV_UNBALANCED_QUOTE;
case '\'':
*(*dest_p) = NUL;
return STR2AV_OK;
case '\\':
ch = *((*src_p)++);
switch (ch) {
case NUL:
return STR2AV_UNBALANCED_QUOTE;
default:
/*
* unknown/invalid escape. Copy escape character.
*/
*((*dest_p)++) = '\\';
break;
case '\\':
case '\'':
break;
}
/* FALLTHROUGH */
default:
*((*dest_p)++) = ch;
break;
}
}
}
static char
escape_convt(char ** src_p)
{
char ch = *((*src_p)++);
/*
* Escape character is always eaten. The next character is sometimes
* treated specially.
*/
switch (ch) {
case 'a': ch = '\a'; break;
case 'b': ch = '\b'; break;
case 't': ch = '\t'; break;
case 'n': ch = '\n'; break;
case 'v': ch = '\v'; break;
case 'f': ch = '\f'; break;
case 'r': ch = '\r'; break;
}
return ch;
}
static str_to_argv_err_t
copy_cooked_string(char ** dest_p, char ** src_p)
{
for (;;) {
char ch = *((*src_p)++);
switch (ch) {
case NUL: return STR2AV_UNBALANCED_QUOTE;
case '"':
*(*dest_p) = NUL;
return STR2AV_OK;
case '\\':
ch = escape_convt(src_p);
if (ch == NUL)
return STR2AV_UNBALANCED_QUOTE;
/* FALLTHROUGH */
default:
*((*dest_p)++) = ch;
break;
}
}
}
This is a variation on litb's answer, but without all the manual memory allocation. It still won't handle quoting.
#include <vector>
#include <string>
#include <sstream>
std::string cmd = "mycommand arg1 arg2";
std::istringstream ss(cmd);
std::string arg;
std::list<std::string> ls;
std::vector<char*> v;
while (ss >> arg)
{
ls.push_back(arg);
v.push_back(const_cast<char*>(ls.back().c_str()));
}
v.push_back(0); // need terminating null pointer
execv(v[0], &v[0]);
I feel kind of dirty about the const_cast<>, but programs really shouldn't be modifying the contents of the argv strings.
You can use the c_str() function of std::string to convert to char*.
The strtok function will split the string using the ' ' delimiter.
Matt Peitrek's LIBTINYC has a module called argcargv.cpp that takes a string and parses it out to the argument array taking quoted arguments into account. Note that it's Windows-specific, but it's pretty simple so should be easy to move to whatever platform you want.
If you do that, also change it to take as parameters the loaction to put the count and the a pointer to the argv array instead of using externs (just my little bit of advice). Matt didn't need that because LIBTINYC was the runtime.
Alternatively, you can look in your compiler's runtime source (nearly all provide it) to see what they do to parse the commandline and either call that directly (if that turns out to be workable) or borrow the ideas from that bit of code.
May be it is too late to answer on this question but you could use standart POSIX functions glob or wordexp:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <wordexp.h>
int
main(int argc, char **argv)
{
wordexp_t p;
char *exec_path = "/bin/ls";
p.we_offs = 1;
wordexp("-l -t /etc", &p, WRDE_DOOFFS);
p.we_wordv[ 0 ] = exec_path;
execv(exec_path, p.we_wordv);
/* This code is unreachable */
exit(EXIT_SUCCESS);
}
It would prepare 3 parameters: -l (long listing format), -t (sort by modification time) and directory /etc to list, and run /bin/ls. Call wordexp() gives you exactly the same result as call /bin/sh -c recomended previously but spawaned process would have parent process not /bin/sh.
As it turned out a function exist somewhat hidden in boost program options for this.
The split_unix() function works with escaped and quoted command lines.
#include "boost/program_options/parsers.hpp"
auto parts = boost::program_options::split_unix(commandLine);
std::vector<char*> cstrings ;
for(auto& str : parts){
cstrings.push_back(const_cast<char*> (str.c_str()));
}
int argc = (int)cstrings.size();
char** argv = cstrings.data();