Delimiting Char Array for Three Variables - c

I'm writing a program to parse a command-line argument into three different parts: host name, file path, and file name, however I am unsure of how to parse a single command-line argument and store the separate parts in three different variables.
I need each portion to create a socket on the client-side of my program. So far I've been able to parse the host name portion, but I get stuck after that.
Is there a way that, after parsing a portion of the string?
EDIT:
The string I'm trying to parse is something like camelot.cba.csuohio.edu/~yourloginid/filename.txt
Here's my code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
int i, sk;
char buf[256], temp[256];
struct sockaddr_in remote;
struct hostent *hp;
if(argc != 2)
{
printf("Invalid number of arguments. Program terminating...");
exit(1);
}
sk = socket(AF_INET, SOCK_STREAM, 0);
remote.sin_family = AF_INET;
strcpy(buf, argv[1]);
for(i = 0; i < strlen(buf); i++)
{
if(buf[i] == '/')
break;
temp[i] = buf[i];
}
hp = gethostbyname(temp);
return 0;
}
EDIT:
I've implemented a while loop to achieve what I'm looking for, but I feel like it's sloppy. Is there a way I can improve it?
while(tk != NULL)
{
if(c == 0)
strcpy(host, tk);
else if(c == 1)
strcpy(path, tk);
else
strcpy(fname, tk);
c++;
tk = strtok(NULL, "/");
}

char st[] = "camelot.cba.csuohio.edu/~yourloginid/filename.txt";
char *host, *path, *fname;
char *ch[3];
for (int i=0; i < 3; ++i) {
ch[i] = strtok(st, "/");
(if ch[i] == NULL) break;
printf("%s\n", ch[i]);
}
if (ch[0] != NULL) {
host = ch[0];
}
if (ch[1] != NULL) {
path = ch[1];
}
if (ch[2] != null) {
path = ch[2];
}
Output:
camelot.cba.csuohio.edu
~yourloginid
filename.txt

You can parse that with strtok
A rough example for you case would be
const char s[2] = "/";
char *token;
/* get the first token */
token = strtok(argv[1], s);
/* walk through other tokens */
while( token != NULL )
{
printf( " %s\n", token );
token = strtok(NULL, s);
}
I didn't compile it but I hope you can use it as an example.
Here you have a complete example of how to use it
http://www.tutorialspoint.com/c_standard_library/c_function_strtok.htm
Hope this helps.

When you know the delimiters, never forget you have simple pointer arithmetic available to you to split/parse any sting. strtok and sscanf are fine tools, but you can do the same thing manually. Here is a short example to add to your list:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXS 128
int main (int argc, char **argv) {
if (argc < 2 ) {
fprintf (stderr, "Error: insufficient input, usage: %s host,path,file\n", argv[0]);
return 1;
}
char *line = strdup (argv[1]); /* make a copy of argument string */
if (!line) {
fprintf (stderr, "error: strdup memory allocation/copy failed.\n");
return 1;
}
char *p = line; /* pointer to the argument string */
char *sp = NULL; /* pointer to use as start pointer */
char host[MAXS] = {0}; /* variables to hold tokens */
char path[MAXS] = {0};
char file[MAXS] = {0};
while (*p && *p != ',') p++; /* find the first ',' */
*p++ = 0; /* null-terminate, advance pointer */
strcpy (host, line); /* read/copy host name */
sp = p; /* set start pointer at current pos */
while (*p && *p != ',') p++; /* find next ',' */
*p++ = 0; /* null-terminate, advance pointer */
strcpy (path, sp); /* read/copy path */
strcpy (file, p); /* pointer on file, read/copy file */
printf ("\n host: %s\n path: %s\n file: %s\n\n", host, path, file);
free (line); /* free memory allocate by strdup */
return 0;
}
Output
$ ./bin/split_host_path_file hostname,pathname,filename
host: hostname
path: pathname
file: filename
Updated to prevent potential read beyond end of line with p.

you can also parse with strtok_r as follows, since strtok is not thread safe.
const char *delim="/";
char *str, *savePtr;
char hosts[3][32];
int i;
for(i=0,str=strtok_r(argv[1], delim, &savePtr);(str!=NULL);str=strtok_r(NULL, delim, &savePtr), i++)
{
print("%s\n", str);
strcpy((char *)host[i], (const char *)str);
}
access host array elements, as it will contain the indexed values delimited by "/"

Related

How can you copy a string to non-accessible memory?

char* argv[MAXARGS];
char* buf2=malloc(MAXLINE * sizeof(char));
strcpy(buf2, buf); //buf is string with some words
char* ptr = strtok(buf2, " ");
argv[0]=ptr;
strcpy(argv[0], ptr);
free(buf2);
Like above, I want to copy value of ptr to argv[0] but I can't use strcpy(argv[0],ptr) directly because accessing argv[0] without argv[0]=ptr cause segmentation fault. So I made code like above but then, after I free buf2, argv[0] becomes null. How can I copy ptr to argv without using =ptr in advance?
Code:
#define MAXARGS 128
#define MAXLINE 256
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
void eval(char* cmdline);
int parseline(char* buf, char** argv);
int main()
{
char cmdline[MAXLINE]; /* Command line */
char* ret;
while (1) {
/* Read */
printf("mini> ");
ret = fgets(cmdline, MAXLINE, stdin);
if (feof(stdin) || ret == NULL)
exit(0);
/* Evaluate */
eval(cmdline);
}
}
void eval(char* cmdline)
{
char** argv=malloc(MAXARGS*sizeof(char)); /* Argument list execve() */
char buf[MAXLINE]; /* Holds modified command line */
int bg; /* Should the job run in bg or fg? */
pid_t pid; /* Process id */
strcpy(buf, cmdline);
bg = parseline(buf, argv);
free(argv);
}
int parseline(char* buf, char** argv)
{
int argc; /* Number of args */
int bg; /* Background job? */
char* buf2=malloc(MAXLINE * sizeof(char));
while (*buf && (*buf == ' '))
buf++;
buf[strlen(buf) - 1] = ' ';/* Replace trailing '\n' with space */
strcpy(buf2, buf);
/* Build the argv list */
argc = 0;
char* ptr = strtok(buf2, " ");
printf("ptr: %s\n", ptr);
while (ptr != NULL) {
//argv[argc]=ptr;
strcpy(argv[argc++], ptr);
ptr = strtok(NULL, " ");
}
argv[argc] = NULL;
printf("0: %s\n", argv[0]);
/* Ignore blank line */
if (argc == 0)
return 1;
/* Should the job run in the background? */
if ((bg = (*argv[argc - 1] == '&')) != 0)
argv[--argc] = NULL;
free(buf2);
printf("0: %s\n", argv[0]);
if(argv[1]!=NULL)
printf("1: %s\n", argv[1]);
return bg;
}
Many errors in your code - I will not check everything only your issue.
Wrong allocation:
char** argv=malloc(MAXARGS*sizeof(char));
You need to allocate space for char pointers - you allocate for char. It is better to use objects instead of types.
char **argv=malloc(MAXARGS * sizeof(*argv));
Now you have allocated the memory for pointers - but not for char arrays to accommodate the strings. To directly copy to argv[n] you need to allocate this memory:
argv[n] = malloc(sizeof(**argv) * (strlen(ptr)+1));
if(argv[n]) strcpy(argv[n], ptr);
In your code you never check the result of malloc - you need to do it after every allocation/reallocation

Extracting the first two words in a sentence in C without pointers

I am getting used to writing eBPF code as of now and want to avoid using pointers in my BPF text due to how difficult it is to get a correct output out of it. Using strtok() seems to be out of the question due to all of the example codes requiring pointers. I also want to expand it to CSV files in the future since this is a means of practice for me. I was able to find another user's code here but it gives me an error with the BCC terminal due to the one pointer.
char str[256];
bpf_probe_read_user(&str, sizeof(str), (void *)PT_REGS_RC(ctx));
char token[] = strtok(str, ",");
char input[] ="first second third forth";
char delimiter[] = " ";
char firstWord, *secondWord, *remainder, *context;
int inputLength = strlen(input);
char *inputCopy = (char*) calloc(inputLength + 1, sizeof(char));
strncpy(inputCopy, input, inputLength);
str = strtok_r (inputCopy, delimiter, &context);
secondWord = strtok_r (NULL, delimiter, &context);
remainder = context;
getchar();
free(inputCopy);
Pointers are powerful, and you wont be able to avoid them for very long. The time you invest in learning them is definitively worth it.
Here is an example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
Extracts the word with the index "n" in the string "str".
Words are delimited by a blank space or the end of the string.
}*/
char *getWord(char *str, int n)
{
int words = 0;
int length = 0;
int beginIndex = 0;
int endIndex = 0;
char currentchar;
while ((currentchar = str[endIndex++]) != '\0')
{
if (currentchar == ' ')
{
if (n == words)
break;
if (length > 0)
words++;
length = 0;
beginIndex = endIndex;
continue;
}
length++;
}
if (n == words)
{
char *result = malloc(sizeof(char) * length + 1);
if (result == NULL)
{
printf("Error while allocating memory!\n");
exit(1);
}
memcpy(result, str + beginIndex, length);
result[length] = '\0';
return result;
}else
return NULL;
}
You can easily use the function:
int main(int argc, char *argv[])
{
char string[] = "Pointers are cool!";
char *word = getWord(string, 2);
printf("The third word is: '%s'\n", word);
free(word); //Don't forget to de-allocate the memory!
return 0;
}

Unable to run ./program with exec and makeargv in c

So to start off, this program has two main parts. The first takes input from the command line using fgets and then makes an argv array with a function called makeargv. The second, takes that argv array and runs it using execvp. The problem I am having is that the program will only run system programs such as "ls", "pwd", "vim", etc. but will not run any program when the directory is specified, such as "./program". I have already tried different versions of exec but the only difference that has made is that then my program will no longer run any commands.
For the below program I cut out all the code that was not relevant to the question to avoid confusion.
#ifndef MAX_CANON
#define MAX_CANON 8192
#endif
int makeargv(const char *s, const char *delimiters, char ***argvp);
int main (int argc, char *argv[]) {
char cmd[MAX_CANON];
char delim[] = "\t";
char **myargv;
printf("Beginning program...\nEnter a command to execute:\n");
while(fgets(cmd, MAX_CANON, stdin) != NULL){ // Here's where I get input from the command line
/* Remove newline from end of command */
if (*(cmd + strlen(cmd) - 1) == '\n' || *(cmd + strlen(cmd) - 1) == ' ' )
*(cmd + strlen(cmd) - 1) = 0;
/*---- Child Code ----*/
if((p = fork()) == 0){
if (makeargv(cmd, delim, &myargv) == -1) { // Here is where I make the argv array
fprintf(stderr, "Child failed to construct an argument array for %s\n", &cmd[0]);
return 1;
}
fprintf(stderr, "Command is: %s\n", *&myargv[0]);
if(execvp(myargv[0], &myargv[0]) == -1){ // Here is where the error keeps appearing
fprintf(stderr, "Error: Failed to execute command!\n");
return 1;
}
return 0;
}
/*---- Parent Code ----*/
Here is the makeargv code
#include <errno.h>
#include <stdlib.h>
#include <string.h>
int makeargv(const char *s, const char *delimiters, char ***argvp) {
int error;
int i;
int numtokens;
const char *snew;
char *t;
if ((s == NULL) || (delimiters == NULL) || (argvp == NULL)) {
errno = EINVAL;
return -1;
}
*argvp = NULL;
snew = s + strspn(s, delimiters); /* snew is real start of string */
if ((t = malloc(strlen(snew) + 1)) == NULL)
return -1;
strcpy(t, snew);
numtokens = 0;
if (strtok(t, delimiters) != NULL) /* count the number of tokens in s */
for (numtokens = 1; strtok(NULL, delimiters) != NULL; numtokens++) ;
/* create argument array for ptrs to the tokens */
if ((*argvp = malloc((numtokens + 1)*sizeof(char *))) == NULL) {
error = errno;
free(t);
errno = error;
return -1;
}
/* insert pointers to tokens into the argument array */
if (numtokens == 0)
free(t);
else {
strcpy(t, snew);
**argvp = strtok(t, delimiters);
for (i = 1; i < numtokens; i++)
*((*argvp) + i) = strtok(NULL, delimiters);
}
*((*argvp) + numtokens) = NULL; /* put in final NULL pointer */
return numtokens;
}
Edit:
Swapped fprintf for perror.
if(execvp(myargv[0], &myargv[0]) == -1){ // Here is where the error keeps appearing
fprintf(stderr, "Error: Failed to execute command!\n");
return 1;
}
if(execvp(myargv[0], &myargv[0]) == -1){ // Here is where the error keeps appearing
perror("Error: Failed to execute command!\n");
return 1;
}
I am now getting a "No such file or directory" error.
FIXED:
The makeargv program was using "\t" as it's delimeter instead of " " so it was not creating the array correctly.
Changing:
char delim[] = "\t";
To:
char delim[] = " ";
Fixes the problem.
There is 2 type of exec:
the first need a path as the environment PATH who contain a location (where find sys executables)
and the others how take real file location.
Form the man page (https://linux.die.net/man/3/exec):
The execlp(), execvp(), and execvpe() functions duplicate the actions of the shell in searching for an executable file if the specified filename does not contain a slash (/) character.
So this mean that you need to use an absolute location, who always begin with /.
You can use getwd() and strcat() to concatenate the 2 strings.
For execvp:
int execvp(const char *file, char *const argv[]); // From the man (I <3 mans)
So:
char argv[3][] = { "/bin/ls", "/", NULL};
if (execvp("/bin/ls", argv) == -1)
return (1);
For evecv:
int execv(const char *path, char *const argv[]);
There is a problem, it need the system PATH. If you don't know what it is type "echo $PATH" in bash. It's a list of directories where the system can find binaries like "ls" in "/bin", concatenate with ":" as sparator. I find the exact definition here.
For your makeargv:
I don't understand why you deference it and take the address after.
execvp(myargv[0], &myargv[0])
char **myargv; //That's we have
char *const argv[]; //that's we need
//so you should try simply:
execvp(myargv[0], myargv);
//or if there is a cast problem
execvp(myargv[0], (char *const[])myargv);
Good luck!

Getting folder from a path

Let's say that I have a path as a string (like this one):
/ROOT/DIRNAME/FILE.TXT
How can I get the parent folder of file.txt (DIRNAME in this case)?
For a path that should have at least one directory in it:
char str[1024]; // arbitrary length. just for this example
char *p;
strcpy(str, "/ROOT/DIRNAME/FILE.TXT"); // just get the string from somewhere
p = strrchr(str, '/');
if (p && p != str+1)
{
*p = 0;
p = strrchr(p-1, '/');
if (p)
print("folder : %s\n", p+1); // print folder immediately before the last path element (DIRNAME as requested)
else
printf("folder : %s\n", str); // print from beginning
}
else
printf("not a path with at least one directory in it\n");
Locate last occurrence of / using strrchr. Copy everything from beginning of string to the found location. Here is the code:
char str[] = "/ROOT/DIRNAME/FILE.TXT";
char * ch = strrchr ( str, '/' );
int len = ch - str + 1;
char base[80];
strncpy ( base, str, len );
printf ( "%s\n", base );
Working just with string; no knowledge of symlink or other types assumed.
You can also do it simply using pointers. Just iterate to the end of the path and then backup until you hit a /, replace it with a null-terminating char and then print the string:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char *argv[]) {
if (argc < 2 ) {
fprintf (stderr, "Error: insufficient input, usage: %s path\n", argv[0]);
return 1;
}
char *path = strdup (argv[1]);
char *p = path;
while (*p != 0) p++;
while (--p)
if (*p == '/') {
*p = 0;
break;
}
printf ("\n path = %s\n\n", path);
if (path) free (path);
return 0;
}
output:
$ ./bin/spath "/this/is/a/path/to/file.txt"
path = /this/is/a/path/to

How to copy front part of string up to a delimiter

I need to grab the first part of a string up to and including the last backslash in a path. I am fairly new to C. So I was wondering if the following code is a good approach? Or is there a better way?
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
char szPath[260] = {0};
strcpy(szPath, argv[0]);
char* p = szPath;
size_t len = strlen(argv[0]);
p+=len; //go to end of string
int backpos = 0;
while(*--p != '\\')
++backpos;
szPath[len-backpos] = 0;
printf("%s\n", szPath);
return 0;
}
After receiving comments changed to this:
char szPath[260];
strcpy(szPath, argv[0]);
/*Scan a string for the last occurrence of a character.*/
char *p = strrchr(szPath, '\\');
if (p) {
*(p + 1) = 0; /* retain backslash and null terminate after that */
} else {
/* handle error */
}
printf("%s\n", szPath);
I would go with strrchr. This assumes str points to writable memory:
char *p;
if ((p = strrchr(str, '\\'))
*(p + 1) = 0; /* Since we passed it to strrchr, it's 0-terminated. */
Obviously, basename and dirname might be there if you are working with paths and might be more appropriate.

Resources