All objects have the same name - c

I'm doing my final project for my algorithms course in C. For the project, we have to take an input text file that contains lines like:
P|A|0
or
E|0|1|2
The former indicates a vertex to be added to the graph we're using in the program, the 2nd token being the name of the vertex, and the last token being its index in the vertices[] array of the graph struct.
I've got a while loop going through this program line by line, it takes the first token to decide whether to make a vertex or an edge, and then proceeds accordingly.
When I finish the file traversal, I call my show_vertices function, which is just a for-loop that prints each name (g->vertices[i].name) sequentially.
The problem is that where the name should go in the output (%s), I keep getting the last "token1" I collected. In the case of the particular input file I'm using it happens to be the source node of the last edge in the list...which is odd because there are two other values passed through the strtok() function afterward. The line in the file looks like:
E|6|7|1
which creates an edge from indexes 6 to 7 with a weight of 1. The edge comes up fine. But when I call any printf with a %s, it comes up "6". Regardless.
This is the file traversal.
fgets(currLn, sizeof(currLn), infile);
maxv = atoi(currLn);
if(maxv = 0)
{
//file not formatted correctly, print error message
return;
}
t_graph *g = new_graph(maxv, TRUE);
while((fgets(currLn, sizeof(currLn), infile)) != NULL)
{
token1 = strtok(currLn, "|");
key = token1[0];
if(key == 'P' || key == 'p')
{
token1 = strtok(NULL, "|");
if(!add_vertex(g, token1))
{
//file integration fail, throw error!
return;
}
//***If I print the name here, it works fine and gives me the right name!****
continue;
}
if(key == 'E' || key == 'e')
{
token1 = strtok(NULL, "|");
token2 = strtok(NULL, "|");
token3 = strtok(NULL, "|");
src = atoi(token1);
dst = atoi(token2);
w = atoi(token3);
if(!add_edge(g, src, dst, w))
{
//file integration fail, throw error
return;
}
continue;
}
else
{
//epic error message because user doesn't know what they're doing.
return;
}
}
If I run show_vertices here, I get:
0. 6
1. 6
2. 6
etc...

You aren't copying the name. So you end up with a pointer (returned by strtok) to single static array in which you read each line. Since the name is always at offset 2, it that pointer will always be currLn+2. When you traverse and print, that will be the last name you read.
You need to strdup(token1) before passing it to (or in) add_vertex.
No there isn't enough information to be certain this is the answer. But I'll bet money this is it.

Related

Tricky Segmentation faults with BST recursion in C

I'm trying to add strings to a Binary Search Tree using a recursive insert method (the usual for BSTs, IIRC) so I can later print them out using recursion as well.
Trouble is, I've been getting a segmentation faults I don't really understand. Related code follows (this block of code is from my main function):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// Stores the size of the C-strings we will use;
// Standardized to 100 (assignment specifications say
// ALL strings will be no more than 100 characters long)
// Please note that I defined this as a preprocessor
// directive because using the const keyword makes it
// impossible to define the size of the C-String array
// (C doesn't allow for static array struct members whose
// size is given as a variable)
#define STRING_SIZE 100
// The flags for case sensitivity
// and an output file
int cflag = 0, oflag = 0;
// These are intended to represent the boolean
// values true and false (there's no native bool
// data type in C, apparently)
const int TRUE = 1;
const int FALSE = 0;
// Type alias for the bool type
typedef int bool;
// This is the BST struct. A BST is basically just
// a Node with at most two children (left and right)
// and a data element.
typedef struct BST
{
struct BST *left;
struct BST *right;
char *key;
int counter;
} BST;
// ----- FUNCTION PROTOTYPES -----
void insert(BST **root, char *key);
int caseSenStrCmp(char *str1, char *str2);
int caseInsenStrCmp(char *str1, char *str2);
bool existsInTree(BST *root, char *key, int cflag);
void inOrderPrint(BST *root, int oflag, FILE *outFile);
void deallocateTree(BST *root);
int main(int argc, char **argv) {
extern char *optarg;
extern int optind;
int c, err = 0;
// Holds the current line in the file/user-provided string.
char currentLine[STRING_SIZE];
// This will store the input/output file
// directories
char fileDirectory[STRING_SIZE];
static char usage[] = "Usage: %s [-c] [-o output_file_name] [input_file_name]\n";
while ((c = getopt(argc, argv, "co:")) != -1)
switch (c)
{
case 'c':
cflag = 1;
break;
case 'o':
oflag = 1;
// If an output file name
// was entered, copy it
// to fileDirectory
if (argv[optind] != NULL)
{
strcpy(fileDirectory, argv[optind]);
}
break;
case '?':
err = 1;
break;
default:
err = 1;
break;
}
if (err)
{
// Generic error message
printf("ERROR: Invalid input.\n");
fprintf(stderr, usage, argv[0]);
exit(1);
}
// --- BST SORT CODE STARTS HERE ---
printf("This is BEFORE setting root to NULL\n");
// This is the BST. As the assignment instructions
// specify, it is initially set to NULL
BST *root = NULL;
// Pointer to the mode the files
// will be opened in. Starts as
// "w" since we're opening the output file
// first
printf("This is AFTER setting root to NULL\n");
char *mode = (char*)malloc(sizeof(char*));
strcpy(mode, "w");
printf("Wrote w to mode pointer");
// Pointer to the output file
FILE *outFile;
// Attempt to open output file
outFile = fopen(fileDirectory, mode);
printf("Opened outfile \n");
// Now update mode and fileDirectory so
// we can open the INPUT file
strcpy(mode, "r");
printf("Wrote r to mode\n");
// Check if we have an input file name
// If argv[optind] isn't NULL, that means we have
// an input file name, so copy it into fileDirectory
if (argv[optind] != NULL)
{
strcpy(fileDirectory, argv[optind]);
}
printf("Wrote input file name to fileDirectory.\n");
// Pointer to the input file
FILE *inFile;
// Attempt to open the input file
//printf("%d", inFile = fopen(fileDirectory, mode));
printf("Opened input file\n");
// If the input file was opened successfully, process it
if (inFile != NULL)
{
// Process the file while EOF isn't
// returned
while (!feof(inFile))
{
// Get a single line (one string)
//fgets(currentLine, STRING_SIZE, inFile);
printf("Wrote to currentLine; it now contains: %s\n", currentLine);
// Check whether the line is an empty line
if (*currentLine != '\n')
{
// If the string isn't an empty line, call
// the insert function
printf("currentLine wasn't the NULL CHAR");
insert(&root, currentLine);
}
}
// At this point, we're done processing
// the input file, so close it
fclose(inFile);
}
// Otherwise, process user input from standard input
else
{
do
{
printf("Please enter a string (or blank line to exit): ");
// Scanf takes user's input from stdin. Note the use
// of the regex [^\n], allowing the scanf statement
// to read input until the newline character is encountered
// (which happens when the user is done writing their string
// and presses the Enter key)
scanf("%[^\n]s", currentLine);
// Call the insert function on the line
// provided
insert(&root, currentLine);
} while (caseSenStrCmp(currentLine, "") != 0);
}
// At this point, we've read all the input, so
// perform in-order traversal and print all the
// strings as per assignment specification
inOrderPrint(root, oflag, outFile);
// We're done, so reclaim the tree
deallocateTree(root);
}
// ===== AUXILIARY METHODS ======
// Creates a new branch for the BST and returns a
// pointer to it. Will be called by the insert()
// function. Intended to keep the main() function
// as clutter-free as possible.
BST* createBranch(char *keyVal)
{
// Create the new branch to be inserted into
// the tree
BST* newBranch = (BST*)malloc(sizeof(BST));
// Allocate memory for newBranch's C-string
newBranch->key = (char*)malloc(STRING_SIZE * sizeof(char));
// Copy the user-provided string into newBranch's
// key field
strcpy(newBranch->key, keyVal);
// Set newBranch's counter value to 1. This
// will be incremented if/when other instances
// of the key are inserted into the tree
newBranch->counter = 1;
// Set newBranch's child branches to null
newBranch->left = NULL;
newBranch->right = NULL;
// Return the newly created branch
return newBranch;
}
// Adds items to the BST. Includes functionality
// to verify whether an item already exists in the tree
// Note that we pass the tree's root to the insert function
// as a POINTER TO A POINTER so that changes made to it
// affect the actual memory location that was passed in
// rather than just the local pointer
void insert(BST **root, char *key)
{
printf("We made it to the insert function!");
// Check if the current branch is empty
if (*root == NULL)
{
// If it is, create a new
// branch here and insert it
// This will also initialize the
// entire tree when the first element
// is inserted (i.e. when the tree is
// empty)
*root = createBranch(key);
}
// If the tree ISN'T empty, check whether
// the element we're trying to insert
// into the tree is already in it
// If it is, don't insert anything (the
// existsInTree function takes care of
// incrementing the counter associated
// with the provided string)
if (!existsInTree(*root, key, cflag))
{
// If it isn't, check if the case sensitivity
// flag is set; if it is, perform the
// checks using case-sensitive string
// comparison function
if (cflag) {
// Is the string provided (key) is
// greater than the string stored
// at the current branch?
if (caseSenStrCmp((*root)->key, key))
{
// If so, recursively call the
// insert() function on root's
// right child (that is, insert into
// the right side of the tree)
// Note that we pass the ADDRESS
// of root's right branch, since
// the insert function takes a
// pointer to a pointer to a BST
// as an argument
insert(&((*root)->right), key);
}
// If not, the key passed in is either less than
// or equal to the current branch's key,
// so recursively call the insert()
// function on root's LEFT child (that is,
// insert into the left side of the tree)
else
{
insert(&((*root)->left), key);
}
}
// If it isn't, perform the checks using
// the case-INsensitive string comparison
// function
else {
// The logic here is exactly the same
// as the comparisons above, except
// it uses the case-insensitive comparison
// function
if (caseInsenStrCmp((*root)->key, key))
{
insert(&((*root)->right), key);
}
else
{
insert(&((*root)->left), key);
}
}
}
}
// CASE SENSITIVE STRING COMPARISON function. Returns:
// -1 if str1 is lexicographically less than str2
// 0 if str1 is lexicographically equal to str2
// 1 if str2 is lexicographically greater than str1
I'm using getopt to parse options that the user enters. I've been doing a little bit of basic debugging using printf statements just to see how far I get into the code before it crashes, and I've more or less narrowed down the cause. It seems to be this part here:
do
{
printf("Please enter a string (or blank line to exit): ");
// Scanf takes user's input from stdin. Note the use
// of the regex [^\n], allowing the scanf statement
// to read input until the newline character is encountered
// (which happens when the user is done writing their string
// and presses the Enter key)
scanf("%[^\n]s", currentLine);
// Call the insert function on the line
// provided
insert(&root, currentLine);
} while (caseSenStrCmp(currentLine, "\n") != 0);
Or rather, calls to the insert function in general, since the printf statement I put at the beginning of the insert function ("We made it to the insert function!) gets printed over and over again until the program finally crashes with a segmentation fault, which probably means the problem is infinite recursion?
If so, I don't understand why it's happening. I initialized the root node to NULL at the beginning of main, so it should go directly into the insert functions *root == NULL case, at least on its first call.
Does it maybe have something to do with the way I pass root as a pointer to a pointer (BST **root in parameter list of insert function)? Am I improperly recursing, i.e. is this statement (and others similar to it)
insert(&((*root)->right), key);
incorrect somehow? This was my first guess, but I don't see how that would cause infinite recursion - if anything, it should fail without recursing at all if that was the case? Either way, it doesn't explain why infinite recursion happens when root is NULL (i.e. on the first call to insert, wherein I pass in &root - a pointer to the root pointer - to the insert function).
I'm really stuck on this one. At first I thought it might have something to do with the way I was copying strings to currentLine, since the line
if(*currentLine != '\0')
in the while (!feof(inFile)) loop also crashes the program with a segmentation fault, but even when I commented that whole part out just to test the rest of the code I ended up with the infinite recursion problem.
Any help at all is appreciated here, I've been trying to fix this for over 5 hours to no avail. I legitimately don't know what to do.
**EDIT: Since a lot of the comments involved questions regarding the way I declared other variables and such in the rest of the code, I've decided to include the entirety of my code, at least until the insert() function, which is where the problem (presumably) is. I only omitted things to try and keep the code to a minimum - I'm sure nobody likes to read through large blocks of code.
Barmar:
Regarding fopen() and fgets(): these were commented out so that inFile would remain NULL and the relevant conditional check would fail, since that part of the code also fails with a segmentation fault
createBranch() does initialize both the left and right children of the node it creates to NULL (as can be seen above)
currentLine is declared as an array with a static size.
#coderredoc:
My understanding of it is that it reads from standard input until it encounters the newline character (i.e. user hits the enter button), which isn't recorded as part of the string
I can already see where you were going with this! My loop conditional was set for the do/while loop was set to check for the newline character, so the loop would never have terminated. That's absolutely my fault; it was a carryover from a previous implementation of that block that I forgot to change.
I did change it after you pointed it out (see new code above), but unfortunately it didn't fix the problem (I'm guessing it's because of the infinite recursion happening inside the insert() function - once it gets called the first time, it never returns and just crashes with a segfault).
**
I managed to figure it out - turns out the problem was with the insert() function after all. I rewrote it (and the rest of the code that was relevant) to use a regular pointer rather than a pointer to a pointer:
BST* insert(BST* root, char *key)
{
// If branch is null, call createBranch
// to make one, then return it
if (root == NULL)
{
return createBranch(key);
}
// Otherwise, check whether the key
// already exists in the tree
if (!existsInTree(root, key, cflag))
{
// If it doesn't, check whether
// the case sensitivity flag is set
if (cflag)
{
// If it is, use the case-sensitive
// string comparison function to
// decide where to insert the key
if (caseSenStrCmp(root->key, key))
{
// If the key provided is greater
// than the string stored at the
// current branch, insert into
// right child
root->right = insert(root->right, key);
}
else
{
// Otherwise, insert into left child
root->left = insert(root->left, key);
}
}
// If it isn't, use the case-INsensitive string
// comparison function to decide where to insert
else
{
// Same logic as before. If the key
// provided is greater, insert into
// current branch's right child
if (caseInsenStrCmp(root->key, key))
{
root->right = insert(root->right, key);
}
// Otherwise, insert into the left child
else
{
root->left = insert(root ->left, key);
}
}
}
// Return the root pointer
return root;
}
Which immediately solved the infinite recursion/seg fault issue. It did reveal a few other minor semantic errors (most of which were probably made in frustration as I desperately tried to fix this problem without rewriting the insert function), but I've been taking care of those bit by bit.
I've now got a new problem (albeit probably a simpler one than this) which I'll make a separate thread for, since it's not related to segmentation faults.

Recursive function : abort-condition

We need to create a binary tree which contains content of textfiles. The pointer selection_a and selection_b pointing to another textfile in the directory.
The structure of the textfiles is following:
line: Title
line: OptionA
line: OptionB
line: Text.
The first file is given as parameter while starting the program. All files should be saved at the beginning of the program. Then the text of the first file shows, and the user can input A or B to continue. Based on the selection, the text of File Option A/B is shown and the user can decide again.
The last file of a tree contains no Options: lines 2 and 3 are "-\n".
The problem is, this code only reads all the option A files of the first tree. It doesn't read in any B-Options. In the end, the program shows a memory access error.
I think the problem is that the readingRows function has no abort condition.
current->selection_a = readingRows(input_selection_a);
current->selection_b = readingRows(input_selection_b);
I know the code may be kind of chaotic, but we are beginners in programming. Hope anybody can help us to write an abort-condition.
The function should be aborted if the content of option A (line 3) is "-\n".
Here is the whole function:
struct story_file* readingRows(FILE *current_file)
{
char *buffer = fileSize(current_file);
char *delimiter = "\n";
char *lines = strtok(buffer, delimiter);
int line_counter = 0;
struct story_file *current = malloc(sizeof(struct story_file));
while(lines != NULL)
{
if(line_counter == 0)
{
current->title = lines;
}
else if(line_counter == 1)
{
char *filename_chapter_a = lines;
FILE *input_selection_a = fopen(filename_chapter_a, "r");
if(input_selection_a)
{
current->selection_a = readingRows(input_selection_a);
}
fclose(input_selection_a);
}
else if(line_counter == 2)
{
char *filename_chapter_b = lines;
FILE *input_selection_b = fopen(filename_chapter_b, "r");
if(input_selection_b)
{
current->selection_b = readingRows(input_selection_b);
}
fclose(input_selection_b);
}
else if (line_counter >= 3)
{
current->text = lines;
}
lines = strtok(NULL, delimiter);
line_counter++;
}
return current;
}
There are two items that define a terminating recursive function:
One or more base cases
Recursive calls that move toward a base case
Your code has one base case: while (lines!=NULL) {} return current;, it breaks the while loop when lines is NULL and returns current. In other words, within any particular call to your function, it only terminates when it reaches the end of a file.
Your code moves toward that base case as long as your files do not refer to each other in a loop. We know this because you always read a line, take an action according to your if-else block, and the read the next line. So you always move toward the end of each file you read.
But as you note, the issue is that you don't have a case to handle "no Options", being when lines 2 or 3 are "-\n". So right now, even though you move through files, you are always opening files in line 2. Unless a file is malformed and does not contain a line 2, your recursive call tree never ends. So you just need to add another base case that looks at whether the beginning of lines matches "-\n", and if it does, return before the recursive call. This will end that branch of your recursive tree.
Inside of your while loop, you will need code along the lines of:
if `line_counter` is `2` or `3`
if `lines` starts with your terminating sequence "-\n"
return current
else
`fopen` and make the recursive call
In the parent function that made the recursive call, it will move to the next line and continue as expected.
P.S. Make sure you use free for each malloc you do.

Input Parsing in C & deciding where to forward

So I have the following project i need to work on for school. It involves a server/client communicating where the client sends requests to the server to get certain information. The server gets the req, parses it, and then sends a response based on the type of the request. For example:
GET /apple/fn/tom/ln/sawyer/a/25/id/1234 : This is a request to get the info for the following person who works at the Apple company (/apple):
fn (first name): Tom
ln (last name): Sawyer
a (age): 25
id (ID): 1234
Now the server should accept input, parse it, and return the information requested from its own database. implementing the server/client is not an issue. I need to know the best way to implement the algorithm to deal with input since not all requests would look like the one above. Other examples of requests:
GET /apple/i: This should return info about Apple company (i.e. address, phone number)
GET /apple/e: return number of employees in Apple company
GET /apple/e/id/1234: return info of employee in Apple company with the following id=1234 (which in our example would be Tom Sawyer) i.e. return first name, last name, age, address.
GET /apple/fn/tom/ln/sawyer/a/25/id/1234 : discussed above
SET /apple/fn/tom/ln/sawyer/a/25/id/5678 : update id of this employee to 5678
SET /apple/fn/tom/ln/sawyer/a/23 : update age of this employee to 23
...
I will have to implement different structs for req/response (i.e. a struct for req and another for response) as well as a different function for each of the different request/responses. BUT what is the best way to deal with parsing input && decide which function to send it to? I was told to look into using a parser-generator like Bison but to my understanding this would only help parse the input and break it up into pieces which is not that hard for me since I know I always have the "/" between fields so I can use the function:
strtok( input, "/" );
So main issue I have is how to decide where to send each request. So Assuming I have the following functions:
struct GetEmployeeInfoReq
{
char *fn;
char *ln;
int age;
};
struct GetEmployeeInfoResp
{
int house_num;
int street_num;
char *street_name;
char * postal_code;
int years_worked_here;
};
void GetEmployeeInfo( struct GetEmployeeInfoResp *resp, struct GetEmployeeInfoReq *req );
struct GetCompanyInfoReq
{
...
}
struct GetCompanyInfoResp
{
...
}
void GetCompanyInfo( struct GetCompanyInfoResp *resp, struct GetCompanyInfoReq *req );
Now I know that to call the first function I need the following request:
GET /apple/fn/tom/ln/sawyer/a/25/id/1234
and to call 2nd function I need the following:
GET /apple/i
My question is how to get this done? Off the top of my mind I'm thinking defining a variable for each possible field in the input req and using that so if this is my request:
GET /apple/e/id/1234
then I would have the following values defined and set to true:
bool is_apple = true;
bool is_employee = true;
bool is_id = true;
After I know that this request is:
GET /apple/e/id/<id_num>
AND NOT
GET /apple/e
so i can send it to the proper function.
Is this the correct approach as I'm lost on how to tackle this issue.
Thanks,
Get yourself a large piece of paper and make a diagram about the logic to get a grammar (not actually neede here, you could just parse it, but being a school assignment I assume that it is meant to be build up upon).
Some observations
every input starts with a /
every entry ends with a / except the last one
entries consist of characters and/or digits
empty entries gets you in trouble if you insist on using strtok (thanks to wildplasser, I missed that)
order matters?
Entries are
either key (one entry) or key/value (two consecutive entries).
In other words: the may or may not have an argument
Entries with a meaning
first entry is the company name (what do you do if that's all? Check assignment)
next entry is either
i print company info
e
without arguments: print #employees
with argument: print information about argument, argument must be correct
fn first name as a key, must have a value because of strtok, argument must be a string
ln last name as a key, must have a value because of strtok, argument must be a string
id id as a key, must have a value because of strtok, argument must be a string of digits (an integer so to say but it is still a string at that point) only
a age as a key, must have a value because of strtok, argument must be a string of digits (an integer so to say but it is still a string at that point) only
Examples:
/apple/e
/ start of input
apple company name. Must have an argument, so go on
e something with employers, my or may not have an argument so check.
EOI (end of input) that means that e has no arguments, so print the number of employees.
/apple/id/134
/ start of input
apple company name. Must have an argument, so go on
e something with employers, my or may not have an argument so check.
id is a key and must have a value, that means we need an argument, so check
1234 all digits which is correct value for id
EOI (end of input) no other things, so print the information you have about id-1234
/apple/fn/tom/ln/sawyer/a/25/id/1234
/ start of input
apple company name. Must have an argument, so go on
fn is a key, must have a value, check
tom the value for fn must be a string which it is, safe
ln is a key, must have a value, check
sawyer the value for ln must be a string which it is, safe
a is a key, must have a value, check
25 the value for ln must be a string of digits which it is, safe
id is a key, must have a value, check
1234 the value for ln must be a string of digits which it is, safe
EOI (end of input). The key for the database entry seems to be the name fn and ln, so read the entry tomsawyer and check if any of id or a is different and change accordingly. If nothing is different: check your assignment.
It might be good idea to build a struct with all of the informations and fill it while parsing (where I wrote "safe"). You need two main functions printIt(keys) and changeIt(key, value). Some things can be done immediately, like in my first example, some need further ado.
That's it, should be straightforward to implement.
EDIT a short example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// ALL CHECKS OMMITTED!
#define DELIMITER '/'
// should resemble a row from the DB
typedef struct company {
char *comp_name;
unsigned int num_empl;
unsigned int empl_id;
unsigned int empl_age;
char *first_name;
char *last_name;
} company;
int main(int argc, char **argv)
{
company *entries;
char *input, *token;
size_t ilen;
if (argc < 2) {
fprintf(stderr, "Usage: %s stringtoparse \n", argv[0]);
exit(EXIT_FAILURE);
}
// work on copy
ilen = strlen(argv[1]);
input = malloc(ilen + 1);
strcpy(input, argv[1]);
entries = malloc(sizeof(company));
// skip first delimiter
if (*input == DELIMITER) {
input++;
ilen--;
}
token = strtok(input, "/");
if (token == NULL) {
fprintf(stderr, "Usage : %s stringtoparse \n", argv[0]);
exit(EXIT_FAILURE);
}
// first entry is the company name
entries->comp_name = malloc(strlen(token));
strcpy(entries->comp_name, token);
// mark empty entries as empty
entries->first_name = NULL;
entries->last_name = NULL;
entries->empl_age = -1;
entries->empl_id = -1;
// F(23)
entries->num_empl = 28657;
// only very small part of grammar implemented for simplicity
for (;;) {
token = strtok(NULL, "/");
if (token == NULL) {
break;
}
// "e" [ "/" "id" "/" number <<EOF>> ]
if (strcmp(token, "e") == 0) {
token = strtok(NULL, "/");
if (token == NULL) {
puts("Info about number of employees wanted\n");
// pure info, pull from DB (not impl.) and stop
break;
} else {
if (strcmp(token, "id") != 0) {
fprintf(stderr, "Only \"id\" allowed after \"e\" \n");
// free all heap memory here
exit(EXIT_FAILURE);
}
token = strtok(NULL, "/");
if (token == NULL) {
fprintf(stderr, "ERROR: \"id\" needs a number \n");
// free all heap memory here
exit(EXIT_FAILURE);
}
// does not check if it really is a number, use strtol() in prod.
entries->empl_id = atoi(token);
printf("Info about employee with id %d wanted\n", entries->empl_id);
// pure info, pull from DB (not impl.) and stop
break;
}
}
// "a" "/" number
else if (strcmp(token, "a") == 0) {
token = strtok(NULL, "/");
if (token == NULL) {
fprintf(stderr, "ERROR: \"a\" needs a number \n");
// free all heap memory here
exit(EXIT_FAILURE);
}
// does not check if it actually is a number, use strtol() in prod.
entries->empl_age = atoi(token);
printf("Age given: %d\n", entries->empl_age);
}
// "id" "/" number
else if (strcmp(token, "id") == 0) {
token = strtok(NULL, "/");
if (token == NULL) {
fprintf(stderr, "ERROR: \"id\" needs a number \n");
// free all heap memory here
exit(EXIT_FAILURE);
}
// does not check if it actually is a number, use strtol() in prod.
entries->empl_id = atoi(token);
printf("ID given: %d\n", entries->empl_id);
}
// "fn" "/" string
else if (strcmp(token, "fn") == 0) {
token = strtok(NULL, "/");
if (token == NULL) {
fprintf(stderr, "ERROR: \"fn\" needs a string \n");
// free all heap memory here
exit(EXIT_FAILURE);
}
entries->first_name = malloc(strlen(token));
strcpy(entries->first_name, token);
printf("first name given: %s\n", entries->first_name);
}
// "ln" "/" string
else if (strcmp(token, "ln") == 0) {
token = strtok(NULL, "/");
if (token == NULL) {
fprintf(stderr, "ERROR: \"ln\" needs a string \n");
// free all heap memory here
exit(EXIT_FAILURE);
}
entries->last_name = malloc(strlen(token));
strcpy(entries->last_name, token);
printf("last name given: %s\n", entries->last_name);
} else {
fprintf(stderr, "ERROR: Unknown token \"%s\" \n", token);
// free all heap memory here
exit(EXIT_FAILURE);
}
}
printf("\n\nEntries:\nCompany name: %s\nFirst name: %s\nLast name: %s\n",
entries->comp_name, entries->first_name, entries->last_name);
printf("Age: %d\nID: %d\nNumber of employees: %d\n",
entries->empl_age, entries->empl_id, entries->num_empl);
/*
* At this state you have information about what is given (in "entries")
* and what is wanted.
*
* Connect to the DB.
*
* If firstnamelastname is the DB-id and in the DB, you can check if
* the given ID is the same as the one in the DB and change if not.
*
* You can do the same for age.
*
* If firstnamelastname is not in the DB but ID is given check if the
* ID is in the DB, change firstname and/or lastname if necessary and
* congratulate on the wedding (many other reasons are possible, please
* check first or it might get really embarassing)
*/
// free all heap memory here
/* Disconnect from the DB */
exit(EXIT_SUCCESS);
}
Compiled with:
gcc -g3 -std=c11 -W -Wall -pedantic jjadams.c -o jjadams
try with
./jjadams "/Apple/fn/Tom/ln/Sawyer/a/10/id/3628800"
./jjadams "/Apple/e"
./jjadams "/Apple/e/id/1234"
./jjadams "/Apple/e/1234"

Breaking a string in C with multiple spaces

Ok, so my code currently splits a single string like this: "hello world" into:
hello
world
But when I have multiple spaces in between, before or after within the string, my code doesn't behave. It takes that space and counts it as a word/number to be analyzed. For example, if I put in two spaces in between hello and world my code would produce:
hello
(a space character)
world
The space is actually counted as a word/token.
int counter = 0;
int index = strcur->current_index;
char *string = strcur->myString;
char token_buffer = string[index];
while(strcur->current_index <= strcur->end_index)
{
counter = 0;
token_buffer = string[counter+index];
while(!is_delimiter(token_buffer) && (index+counter)<=strcur->end_index)//delimiters are: '\0','\n','\r',' '
{
counter++;
token_buffer = string[index+counter];
}
char *output_token = malloc(counter+1);
strncpy(output_token,string+index,counter);
printf("%s \n", output_token);
TKProcessing(output_token);
//update information
counter++;
strcur->current_index += counter;
index += counter;
}
I can see the problem area in my loop, but I'm a bit stumped as to how to fix this. Any help would be must appreciated.
From a coding stand point, if you wanted to know how to do this without a library as an exercise, what's happening is your loop breaks after you run into the first delimeter. Then when you loop to the second delimeter, you don't enter the second while loop and print a new line again. You can put
//update information
while(is_delimiter(token_buffer) && (index+counter)<=strcur->end_index)
{
counter++;
token_buffer = string[index+counter];
}
Use the standard C library function strtok().
Rather than redevelop such a standard function.
Here's the related related manual page.
Can use as following in your case:
#include <string.h>
char *token;
token = strtok (string, " \r\n");
// do something with your first token
while (token != NULL)
{
// do something with subsequents tokens
token = strtok (NULL, " \r\n");
}
As you can observe, each subsequent call to strtok using the same arguments will send you back a char* adressing to the next token.
In the case you're working on a threaded program, you might use strtok_r() C function.
First call to it should be the same as strtok(), but subsequent calls are done passing NULL as the first argument. :
#include <string.h>
char *token;
char *saveptr;
token = strtok_r(string, " \r\n", &saveptr)
// do something with your first token
while (token != NULL)
{
// do something with subsequents tokens
token = strtok_r(NULL, " \r\n", &saveptr)
}
Just put the process token logic into aif(counter > 0){...}, which makes malloc happen only when there was a real token. like this
if(counter > 0){ // it means has a real word, not delimeters
char *output_token = malloc(counter+1);
strncpy(output_token,string+index,counter);
printf("%s \n", output_token);
TKProcessing(output_token);
}

Realloc Causing App Crash

This question has been asked multiple times, but I've done (from what I can tell) everything that's been mentioned here. Basically, I'm getting 1 character at a time from a TCP socket and I'm building a dynamically growing string with the one character. I can do looping prints and see that the string grows and grows, and then when it gets to 20 characters long, the program crashes.
while(FilterAmount != 0)
{
g_io_channel_read_chars (source,(gchar *) ScanLine,1,&BytesRead,&GlibError);
printf("Scanline: %s FilterAmount: %ld\n", ScanLine, FilterAmount);
//the filters are delimited by \n, munch these off, reset important variables, save the last filter which is complete
if(ScanLine[0] == FilterTerminator[0]) {
//if the Filter Name actually has a filter in it
if(FilterName != NULL){
FilterArray = FilterName; //save off the filter name
printf("This is the filter name just added: %s\n", FilterName);
FilterArray++; //increment the pointer to point to the next memory location.
FilterAmount--; //update how many filters we have left
FilterNameCount = 0; //reset how many characters each filter name is
free(FilterName);
free(FilterTmp);
}
}
else {
printf("else!\n");
//keep track of the string length of the filter
FilterNameCount++;
//allocate more memory in the string used to store the filter name + null terminating character
FilterTmp = (gchar*)realloc(FilterName, FilterNameCount*sizeof(char) + 1);
if(FilterTmp == NULL)
{
free(FilterName);
printf("Error reallocating memory for the filter name temporary variable!");
return 1;
}
FilterName = FilterTmp;
printf("filter name: %s\n", FilterName);
//concat the character to the end of the string where space was just made for it.
strcat(FilterName, ScanLine);
}
}
}
This section of code loops and loops whenever we have a non "\n" character in a buffer we're reading data into. The program crashes when allocating the 21st character's location every single time. Here are the pertinent declarations:
static gchar *FilterName = NULL, *FilterTmp = NULL;
static gchar ScanLine[9640];

Resources