Split data from ethernet - c

I need split data from ethernet. Data is in this format:
ZMXXX,angle*CHCK
Where angle is number. For example: ZMXXX,900*5A
And I need separated ZMXXX,900 and 5A. I wrote this function:
void split_data(char analyze[])
{
char *words[5]; uint8_t i=0;
words[i] = strtok(analyze,"*");
while(words[i]!=NULL)
{
words[++i] = strtok(NULL,"*");
}
}
And result is here:
And now, how I can get this data from variable:
words[0]
words[1]

Assuming the format you mention to be fixed, there is no need for the expensive and error-prone strtok().
Use the good old strchr():
int parse(char * input, char ** output)
{
int result = -1; /* Be pessimistic. */
if ((NULL == inout) || (NULL == output))
{
errno = EINVAL;
goto lblExit;
}
char * pc = strchr(analyze, '*');
if (NULL == pc);
{
errno = EINVAL;
goto lblExit;
}
*pc = '\0'; /* Set a temporary `0`-terminator. */
output[0] = strdup(analyze); /* Copy the 1st token. */
if (NULL == output[0])
{
goto lblExit;
}
*pc = '*'; /* Restore the original. */
output[1] = strdup(pc + 1); /* Seek beyond the `*` and copy the 2nd token. */
if (NULL == output[1])
{
free(outout[0]); /** Clean up. */
goto lblExit;
}
result = 0; /* Indicate success. */
lblExit:
return result;
}
Use it like this:
#define _POSIX_C_SOURCE 200809L /* To make strdup() available. */
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int parse(char *, char **);
int main(void)
{
char data[] = "ZMXXX,900*5A";
char * words[2];
if (-1 == parse(data, words))
{
perror("parse() failed");
exit(EXIT_FAILURE);
}
printf("word 1 = '%s'\n", words[0]);
printf("word 2 = '%s'\n", words[1]);
free(words[0]);
free(words[1]);
return EXIT_SUCCESS;
}
The above code is expected to print:
word 1 = 'ZMXXX,900'
word 2 = '5A'
Note that strdup() isn't Standard C, but POSIX. It might need to be activated using one of the appropriate defines.

Related

Trouble reading a .txt file and storing into an array

I've been given a .txt file with a specific structure: each line has a String with 5 characters but with a random number of lines, and we should read the file and store it as we want.
I've tried doing it with a linked list and it worked just fine but as the size of the file grew up, the time it took to execute was too long. Since then i've been trying to store the Strings into an array of strings, so everything would be stored contiguously in memory. When executing, i get a segmentation fault error and i have no idea why. The code goes as follows:
int nLines (char *path)
{
int answer = 0;
FILE* fp;
fp = fopen(path,"r");
char line[6];
while (fgets(line, sizeof(line),fp))
{
answer++;
}
return answer;
}
int main (int argc, char *argv[])
{
FILE* fp;
fp = fopen(argv[1], "r");
int numberLines = nLines(argv[1]);
char **storage = malloc(numberLines * 6 * sizeof(char));
if(storage != NULL)
{
int i = 0;
char line [6];
while (fgets(line, sizeof(line),fp))
{
strcpy(storage[i], line);
i++;
}
}
free(storage);
}
The first function is supposed to return the number of lines there is in the file. With this information, i'm trying to allocate memory equal to the number of strings * the size of each string since i know before hand this value. I'm imagining the problem comes from the line:
char **storage = malloc (numberLines * 6 *sizeof(char));
I haven't touched C in a long time and i'm kinda rusty with the whole pointers and memory stuff. Can someone help please. Thank you!
your allocation is wrong
int main (int argc, char *argv[])
{
FILE* fp;
fp = fopen(argv[1], "r");
size_t numberLines = 0;
char **storage = NULL;
char line [8];
while (fgets(line, sizeof(line),fp))
{
storage = realloc(storage, (numberLines + 1) * sizeof(*storage));
storage[numberLines] = malloc(8);
strcpy(storage[numlines++], line);
}
/* ... */
}
you need to allocate space for the pointers, then space for the strings. It is demo only and you should implement the correct error handling (memory and file).
If one wants to truly have an on-line algorithm, one isn't going to have the number of lines available. The idiomatic way to have a contiguous dynamic container is to reallocate geometrically increasing capacity, like vector or ArrayList. C doesn't have that type built-in, but it's worth the extra code if one uses it a lot. For example, this reads from stdin until EOF and uses a Fibonacci sequence as it's capacities.
#include <stddef.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
/** One line of maximum 5 `char` plus 1 `NUL`. */
struct Line { char str[6]; };
/** A dynamic line array. */
struct LineArray {
struct Line *data; /* data -> (c0 < c1 || c0 == c1 == max_size) */
size_t capacity, next_capacity; /* !data -> !size, data -> size<=capacity */
size_t size;
};
/** Ensures `min_capacity` of `a`. Return success, otherwise, `errno` will be
set: `realloc` or `ERANGE` -- tried allocating more then can fit in `size_t`
or `realloc` doesn't follow [IEEE Std 1003.1-2001
](https://pubs.opengroup.org/onlinepubs/009695399/functions/realloc.html). */
static int reserve(struct LineArray *const a, const size_t min_capacity) {
size_t c0, c1;
struct Line *data;
const size_t max_size = (size_t)-1 / sizeof(struct Line *);
assert(a);
if(!a->data) {
if(!min_capacity) return 1;
c0 = 8, c1 = 13;
} else {
if(min_capacity <= a->capacity) return 1;
c0 = a->capacity, c1 = a->next_capacity;
}
if(min_capacity > max_size) return errno = ERANGE, 0;
assert(c0 < c1); /* Fibonacci: c0 ^= c1, c1 ^= c0, c0 ^= c1, c1 += c0. */
while(c0 < min_capacity) {
size_t temp = c0 + c1; c0 = c1; c1 = temp;
if(c1 > max_size || c1 < c0) c1 = max_size;
}
if(!(data = realloc(a->data, c0 * sizeof *a->data)))
{ if(!errno) errno = ERANGE; return 0; }
a->data = data;
a->capacity = c0;
a->next_capacity = c1;
return 1;
}
/** Adds one to the size of `a` and returns it (`push_back`.) Exceptional
return null and `errno` is `realloc` or `ERANGE`. */
static struct Line *new_line(struct LineArray *const a) {
assert(a);
if(a->size >= (size_t)-1) { errno = ERANGE; return 0; } /* Unlikely. */
if(!reserve(a, a->size + 1)) return 0; /* (Less) unlikely. */
return a->data + a->size++;
}
/** Destructor. */
static void linearray_(struct LineArray *const a) {
assert(a);
free(a->data);
a->data = 0, a->capacity = a->next_capacity = a->size = 0;
}
#include <string.h>
#include <stdio.h>
int main(void)
{
struct LineArray storage = { 0, 0, 0, 0 };
struct Line *s, *s_end;
size_t l = 0, line_len;
char line[7] = "";
int success = EXIT_FAILURE;
/* `line` must be capable of storing the "*[,5]\n\0". */
assert(sizeof line == sizeof ((struct Line *)0)->str + 1);
while (fgets(line, sizeof line, stdin))
{
l++;
line_len = strlen(line);
assert(line_len && line_len < sizeof line);
/* Too long. */
if(line[line_len - 1] != '\n') { errno = ERANGE; goto catch; }
/* Cut off the trailing new-line. */
line[line_len-- - 1] = '\0';
/* Store `line`. */
if(!(s = new_line(&storage))) goto catch;
strcpy(s->str, line);
}
if(ferror(stdin)) goto catch;
/* Print all. */
for(s = storage.data, s_end = s + storage.size; s < s_end; s++)
printf("stored: %s\n", s->str);
success = EXIT_SUCCESS;
goto finally;
catch:
perror("Error");
fprintf(stderr, "On line %lu: \"%s\".\n", (unsigned long)l, line);
finally:
linearray_(&storage);
return success;
}

Understanding expected behavior of c language code snippet

I have been given a c language code
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_BUFFER 256
#define QUIT_STRING "q"
int makeargv(const char *s, const char *delimiters, char ***argvp);
int main (void) {
char **chargv;
char inbuf[MAX_BUFFER];
for( ; ; ) {
gets(inbuf);
if (strcmp(inbuf, QUIT_STRING) == 0)
return 0;
if ((fork() == 0) && (makeargv(inbuf, " ", &chargv) > 0))
execvp(chargv[0], chargv);
wait(NULL);
}
}
makeargv function which makes tokens out of the string passed as 1st argument (using delimiters passed in 2nd argument) and stores these tokens in the array pointed to by the 3rd argument.
#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;
}
and i need to answer 3 questions which are mentioned below
How would the shell implemented in above code will behave when the user gives an invalid command (i.e. a command for which no executable exists)?
What would happen if the user gives multiple invalid commands?
What happens when the user tries to quit the shell after giving multiple invalid commands.
Here's what i think are the answers to these questions
The execv will return an error, but I do not think it will break the code so it will result in two forks trying to read inputs
More forks will be created
Only one of the forks will quit
Question
Are all of the answers correct? Could any of the answers be improved?
Never have two processes trying to read stdin at once. Race conditions make the resulting environment unusable.
if (fork() ==0){
if(...)
execvp();
_exit(255); /* don't fall back to parent code */
}

Is there a fast way to interpose a character between strings?

I wrote this function that will generate a single string out of a file list.
(e.g. if I have a folder with FileA.txt, FileB.png and FileC I'll get as output this string: FileA.txtFileB.pngFileC). Now I want to add a / character between each filename. (e.g. FileA.txt/FileB.png/FileC/) Is there a way to do it in "one blow" without having to repeat the same operation twice?
In other words, is there a way to do something like:
original_string = append2(original_string, new_string, '/');
instead of having to do
append(original_string, new_string);
append(original_string, "/");
?
Here's the function I wrote as reference:
/**
* #brief Concatenate all file names in a file list (putting a '/' between each of them)
* #param file_list The file list to serialize.
* #return A string containing all files in the file list.
*/
char *file_list_tostring(struct file_list *file_list) {
char *final_string = NULL;
size_t final_len = 0;
struct file_node *list_iter = file_list->first;
while (list_iter != NULL) {
char *tmp = list_iter->filename;
size_t tmp_len = strlen(tmp);
char *s = realloc(final_string, final_len + tmp_len + 1); // +1 for '\0'
if (s == NULL) {
perror("realloc");
exit(EXIT_FAILURE);
}
final_string = s;
memcpy(final_string + final_len, tmp, tmp_len + 1);
final_len += tmp_len;
list_iter = list_iter->next;
}
return final_string;
}
Maybe there is a simple way to interpose a single character between two strings?
Note: I know there's nothing wrong in repeating the same operation twice, I'm asking this question to know if there is a better way of doing so!
Yes, you can do sprintf:
#include <stdio.h>
int main()
{
char var1[] = "FileA.txt";
char var2[] = "FileB.png";
char var3[] = "FileC";
char result[30];
sprintf(result, "%s/%s/%s", var1, var2,var3);
printf("result: %s\n", result);
return 0;
}
And the result is like this:
result: FileA.txt/FileB.png/FileC
If you need, the variable result can be a pointer and allocate space based on your needs.
As Michael Burr mentioned in a comment to the question, it is best to walk the list/array twice. On the first pass, calculate the total length of the string needed. Next, allocate the memory needed for the entire string. On the second pass, copy the contents. Do not forget to account for, and append, the string-terminating nul byte (\0).
Consider the following example functions dupcat() and dupcats():
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
char *dupcat(const size_t count, const char *parts[])
{
size_t i, len = 0;
char *dst, *end;
/* Calculate total length of parts. Skip NULL parts. */
for (i = 0; i < count; i++)
len += (parts[i]) ? strlen(parts[i]) : 0;
/* Add room for '\0'.
We add an extra 8 to 15 '\0's, just because
it is sometimes useful, and we do a dynamic
allocation anyway. */
len = (len | 7) + 9;
/* Allocate memory. */
dst = malloc(len);
if (!dst) {
fprintf(stderr, "dupcat(): Out of memory; tried to allocate %zu bytes.\n", len);
exit(EXIT_FAILURE);
}
/* Copy parts. */
end = dst;
for (i = 0; i < count; i++) {
const char *src = parts[i];
/* We could use strlen() and memcpy(),
but a loop like this will work just as well. */
if (src)
while (*src)
*(end++) = *(src++);
}
/* Sanity check time! */
if (end >= dst + len) {
fprintf(stderr, "dupcat(): Arguments were modified during duplication; buffer overrun!\n");
free(dst); /* We can omit this free(), but only in case of exit(). */
exit(EXIT_FAILURE);
}
/* Terminate string (and clear padding). */
memset(end, '\0', (size_t)(dst + len - end));
/* Done! */
return dst;
}
char *dupcats(const size_t count, ...)
{
size_t i, len = 0;
char *dst, *end;
va_list args;
/* Calculate total length of 'count' source strings. */
va_start(args, count);
for (i = 0; i < count; i++) {
const char *src = va_arg(args, const char *);
if (src)
len += strlen(src);
}
va_end(args);
/* Add room for end-of-string '\0'.
Because it is often useful to know you have
at least one extra '\0' at the end of the string,
and we do a dynamic allocation anyway,
we pad the string with 9 to 16 '\0',
aligning 'len' to a multiple of 8. */
len = (len | 7) + 9;
/* Allocate memory for the string. */
dst = malloc(len);
if (!dst) {
fprintf(stderr, "dupcats(): Out of memory; tried to allocate %zu bytes.\n", len);
exit(EXIT_FAILURE);
}
/* Copy the source strings. */
end = dst;
va_start(args, count);
for (i = 0; i < count; i++) {
const char *src = va_arg(args, const char *);
/* We could use strlen() and memcpy() here;
however, this loop is easier to follow. */
if (src)
while (*src)
*(end++) = *(src++);
}
va_end(args);
/* Sanity check. */
if (end >= dst + len) {
fprintf(stderr, "dupcats(): Arguments were modified during duplication; buffer overrun!\n");
free(dst); /* We can omit this free(), but only in case of exit(). */
exit(EXIT_FAILURE);
}
/* Add end-of-string '\0' (filling the padding). */
memset(end, '\0', dst + len - end);
/* Done. */
return dst;
}
int main(int argc, char *argv[])
{
char *result;
result = dupcat(argc - 1, (const char **)(argv + 1));
printf("Arguments concatenated: '%s'.\n", result);
free(result);
result = dupcats(5, "foo", "/", "bar", "/", "baz");
printf("Concatenating 'foo', '/', 'bar', '/', and 'baz': '%s'.\n", result);
free(result);
return EXIT_SUCCESS;
}
Neither dupcat() nor dupcats() will ever return NULL: they will print an error message to standard error and exit, if an error occurs.
dupcat() takes an array of strings, and returns a dynamically allocated concatenated copy with at least eight bytes of nul padding.
dupcats() takes a variable number of pointers, and returns a dynamically allocated concatenated copy with at least eight bytes of nul padding.
Both functions treat NULL pointers as if they were empty strings. For both functions, the first parameter is the number of strings to concatenate.
(Since OP did not show the definitions of struct file_list or struct file_node, I did not bother to write a list-based version. However, it should be trivial to adapt from one of the two versions shown.)
In some cases, a variant that constructs a valid path from a fixed base part, with one or more relative file or directory names concatenated, and POSIXy ./ removed and ../ backtracked (but not out of base subtree), is very useful.
If carefully written, it allows the program to accept untrusted paths, relative to a specific subtree. (The combined paths are confined to that subtree, but symlinks and hardlinks can still be used to escape the subtree.)
One possible implementation is as follows:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
char *dynamic_path(const char *const subtree,
const size_t parts,
const char *part[])
{
const size_t subtree_len = (subtree) ? strlen(subtree) : 0;
size_t parts_len = 0;
size_t total_len, i;
char *path, *mark, *curr;
/* Calculate the length of each individual part.
Include room for a leading slash.
*/
for (i = 0; i < parts; i++)
parts_len += (part[i]) ? 1 + strlen(part[i]) : 0;
/* Add room for the string-terminating '\0'.
We're paranoid, and add a bit more padding. */
total_len = ((subtree_len + parts_len) | 7) + 9;
/* Allocate memory for the combined path. */
path = malloc(total_len);
if (!path) {
errno = ENOMEM;
return NULL;
}
/* If the user specified a subtree, we use it as the fixed prefix. */
if (subtree_len > 0) {
memcpy(path, subtree, subtree_len);
mark = path + subtree_len;
/* Omit a trailing /. We enforce it below anyway. */
if (parts > 0 && subtree_len > 1 && mark[-1] == '/')
--mark;
} else
mark = path;
/* Append the additional path parts. */
curr = mark;
for (i = 0; i < parts; i++) {
const size_t len = (part[i]) ? strlen(part[i]) : 0;
if (len > 0) {
/* Each path part is a separate file/directory name,
so there is an (implicit) slash before each one. */
if (part[i][0] != '/')
*(curr++) = '/';
memcpy(curr, part[i], len);
curr += len;
}
}
/* Sanity check. */
if (curr >= path + total_len) {
/* Buffer overrun occurred. */
fprintf(stderr, "Buffer overrun in dynamic_path()!\n");
free(path); /* Can be omitted if we exit(). */
exit(EXIT_FAILURE);
}
/* Terminate string (and clear padding). */
memset(curr, '\0', (size_t)(path + total_len - curr));
/* Cleanup pass.
Convert "/foo/../" to "/", but do not backtrack over mark.
Combine consecutive slashes and /./ to a single slash.
*/
{
char *src = mark;
char *dst = mark;
while (*src)
if (src[0] == '/' && src[1] == '.' && src[2] == '.' && (!src[3] || src[3] == '/')) {
src += 3; /* Skip over /.. */
/* Backtrack, but do not underrun mark. */
if (dst > mark) {
dst--;
while (dst > mark && *dst != '/')
dst--;
}
/* Never consume the mark slash. */
if (dst == mark)
dst++;
} else
if (src[0] == '/' && src[1] == '.' && (!src[2] || src[2] == '/')) {
src += 2; /* Skip over /. */
if (dst == mark || dst[-1] != '/')
*(dst++) = '/';
} else
if (src[0] == '/') {
src++;
if (dst == mark || dst[-1] != '/')
*(dst++) = '/';
} else
*(dst++) = *(src++);
/* Clear removed part. */
if (dst < src)
memset(dst, '\0', (size_t)(src - dst));
}
return path;
}
int main(int argc, char *argv[])
{
char *path;
if (argc < 2) {
fprintf(stderr, "\nUsage: %s PREFIX [ PATHNAME ... ]\n\n", argv[0]);
return EXIT_FAILURE;
}
path = dynamic_path(argv[1], argc - 2, (const char **)(argv + 2));
if (!path) {
fprintf(stderr, "dynamic_path(): %s.\n", strerror(errno));
return EXIT_FAILURE;
}
printf("%s\n", path);
free(path);
return EXIT_SUCCESS;
}
Note that I wrote the above version from scratch (and dedicate it to public domain (CC0)), so you should thoroughly test it before relying it on production use. (My intent is for it to be an useful example or basis, that will help you write your own implementation tailored to your needs.)
If you do find any bugs or issues in it, let me know in a comment, so I can verify and fix.

Function that gets a chars array separated by commas

I have a char array that i get from serial port (Arduino) .
the data is separated by commas so its like this :
header:data
The chars array that holds this structure is pre defined like this : char content[50];
I am trying to write a function , that will get a parameter content
and return only the header , and then another function to return only the data. I know i have to start with this - but dont really know how to continue:
void getHeader( char* localString)
{
char delimiters[] = ":";
char *valPosition=NULL;
char newString=NULL ;
valPosition = strtok(localString, delimiters); //what is this doing ?
.... //whats now ?
//also how do you return the header to the argument
I have tried that according to an answer here ,but i get nothing to print:
char delimiters[] = ":";
char *valPosition=NULL;
char newString=NULL ;
char * header = NULL;
valPosition = strtok(content, delimiters);
malloc(strlen(valPosition) + 1);
strcpy(header, valPosition);
Serial.println(header);
Let's have a look at the man page for strtok(). It says
char *strtok(char *str, const char *delim);
The strtok() function parses a string into a sequence of tokens..... The delim argument specifies a set of bytes that delimit the tokens in the parsed string. ...... Each call to strtok() returns a pointer to a null-terminated string containing the next token.
That means, when you call
valPosition = strtok(localString, delimiters); /
strtok() will search localString for the delimiter specified in delimiters
and if it finds any, it will return the token as a null-terminated string.
Beware, strtok()
modify their first argument.
and
cannot be used on constant strings.
so, the localString should be modifiable, i.e., cannot be a string literal.
Next, as per your format, strtok() will return the header, without the :.
so, you need to copy the returned string into another and return that. You can use dynamic memory allocation, as per following algorithm
Define a char * header = NULL;
Check the returned value of strtok(), if not NULL, allocate memory to header, like header = malloc(strlen(valPosition) + 1);
copy the data using strcpy(header, valPosition);
return the header from the function.
I hope, you understand that you need to change the function prototype also return a pointer, like
char * getHeader( char* localString){....
Also, once you're done using the returned value, you need to free() it.
If header:data is the only use case you have you want to look for strchr().
Example:
#include <string.h> /* for strchr() and strlen() */
#include <errno.h> /* for EINVAL */
int get_header_and_data(const char * input, char ** pheader, char ** pdata)
{
int result = 0;
if (NULL == input
|| NULL == pheader || NULL == *pheader
|| NULL == pdata || NULL == *pdata
)
{
/* Trivial case of "no" input and/or missing references to store the result. */
errno = EINVAL;
result = -1;
}
else
{
char * pcolon = strchr(input, ':');
if (NULL == pcolon)
{
/* No delimiter found. */
errno = EINVAL;
result = -1;
}
else
{
/* Delimiter found. */
if (pcolon == input)
{
/* No header found. */
errno = EINVAL;
result = -1;
}
else
{
if (1 == strlen(pcolon))
{
/* No data found. */
errno = EINVAL;
result = -1;
}
else
{
/* Success. */
*pcolon = '\0';
++pcolon;
(*data) = pcolon;
(*pheader) = input;
}
}
}
}
return result;
}
and use it like this:
#include <stdio.h>
int get_header_and_data(const char *, char **, char **);
...
char content[50] = "";
/* Load content here. */
char * header = NULL;
char * data = NULL;
if (-1 == get_header_and_data(content, &header, &data)
{
perror("get_header_and_data() failed.");
abort(); /* Or what ever to handle the error. */
}
else
{
/* Dereference and/or use header and data here .*/
}
Please not that on success header and data (still) refer to the memory of content, as well as that on success the latter is modifed.
Just for fun, the code above can be shrunk down to:
int get_header_and_data(const char * input, char ** pheader, char ** pdata)
{
int result = 0;
if (NULL == input
|| NULL == pheader || NULL == *pheader
|| NULL == pdata || NULL == *pdata
)
{
/* Trivial case of "no" input and/or missing references to store the result. */
errno = EINVAL;
result = -1;
}
else
{
char * pcolon = strchr(input, ':');
if (NULL == pcolon /* No delimiter found. */
|| pcolon == input /* No header found. */
|| 1 == strlen(pcolon) /* No data found. */
)
{
errno = EINVAL;
result = -1;
}
else
{
/* Success. */
*pcolon = '\0';
++pcolon;
(*data) = pcolon;
(*pheader) = input;
}
}
return result;
}

Delimiting Char Array for Three Variables

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 "/"

Resources