I am trying to write a function that will find a node with a specified name in a xml file.
The problem is that the function never finds the specified node.
xmlNodePtr findNodeByName(xmlNodePtr rootnode, const xmlChar * nodename)
{
xmlNodePtr node = rootnode;
if(node == NULL){
log_err("Document is empty!");
return NULL;
}
while(node != NULL){
if(!xmlStrcmp(node->name, nodename)){
return node;
}
else if(node->children != NULL){
node = node->children;
xmlNodePtr intNode = findNodeByName(node, nodename);
if(intNode != NULL){
return intNode;
}
}
node = node->next;
}
return NULL;
}
I can see in the debugger that function does go deep into the sub nodes but still returns NULL.
Thanks in advance.
else if(node->children != NULL) {
node = node->children;
xmlNodePtr intNode = findNodeByName(node, nodename);
if (intNode != NULL) {
return intNode;
}
}
This should be:
else if (node->children != NULL) {
xmlNodePtr intNode = findNodeByName(node->children, nodename);
if(intNode != NULL) {
return intNode;
}
}
and it works fine
Your function is correct. Add a debugging line to see why it doesn't work in your case. For example:
printf("xmlStrcmp(%s, %s)==%d\n", node->name, nodename,
xmlStrcmp(node->name, nodename));
But you don't really need that function. You may use xmlXPathEval.
When #jarekczek mentionned XPath, he means to use XPath instead of your function.
With XPath, your function will become:
xmlNodeSetPtr findNodesByName(xmlDocPtr doc, xmlNodePtr rootnode, const xmlChar* nodename) {
// Create xpath evaluation context
xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
if(xpathCtx == NULL) {
fprintf(stderr,"Error: unable to create new XPath context\n");
return NULL;
}
// The prefix './/' means the nodes will be selected from the current node
const xmlChar* xpathExpr = xmlStrncatNew(".//", nodename, -1);
xmlXPathObjectPtr xpathObj = xmlXPathNodeEval(rootnode, xpathExpr, xpathCtx);
if(xpathObj == NULL) {
fprintf(stderr,"Error: unable to evaluate xpath expression \"%s\"\n", xpathExpr);
xmlXPathFreeContext(xpathCtx);
return NULL;
}
xmlXPathFreeContext(xpathCtx);
return xpathObj->nodesetval;
}
Note: This function returns all the nodes matching 'nodename'. If you only want the first node, you could replace return xpathObj->nodesetval; by:
if (xpathObj->nodesetval->nodeNr > 0) {
return xpathObj->nodesetval->nodeTab[0];
} else {
return NULL;
}
You could also append [1] to the XPath query to optimize your query.
Related
I've been working on a linked list which was implemented by the name vector_string.
I'm trying to insert words into it, if the word is already existed in the linked list, skip it.
void vector_string_insert(vector_string *vs, char *key) {
vs_entry_t *newNode = (vs_entry_t*)malloc(sizeof(vs_entry_t));
newNode->value = key;
newNode->next = NULL;
vs_entry_t *current = vs->head;
if(vs->head == NULL)
{
vs->head = newNode;
}
else{
while(current->next != NULL)
current = current->next;
current->next = newNode;
}
}
bool vector_string_find(vector_string *vs, char *key)
{
vs_entry_t *current = vs->head;
while(current != NULL)
{
if (current->value == key)
return true;
current = current->next;
}
return false;
}
And here's my main:
if (vector_string_find(header, "Hello") == false);
vector_string_insert(header,"Hello");
if (vector_string_find(header, "Hello") == false);
vector_string_insert(header,"Hello");
if (vector_string_find(header, "Hola") == false);
vector_string_insert(header,"Hola");
Although the vector_string_find() returned "true" for the second "Hello", it still got inserted in the linked list:
1. Hello
2. Hello
3. Hola
Suppose we have the following structure:
typedef struct link {
char link[MAXLINK];
struct link *next;
} LinkT;
typedef struct node {
char keyword[MAXKEY];
LinkT *links;
struct node *next;
} NodeT;
In the main function I have added few new keyword and links to it. The output I'm getting when printing the whole list is:
Asteroid
Link-45
Link-23
Earth
Link-2
Moon
Link-3
Link-1
I would like to add a new link-4 to the Earth. So the output would look like this:
Asteroid
Link-45
Link-23
Earth
Link-2
Link-4
Moon
Link-3
Link-1
My idea was to have a findLink function that traverse through the list of keywords, and then ones keyword matches, traverse through the links list and if no link was found, I add new one.
However, the problem is when I run the below code, it never finishes, does not output anything (freezing?) and I have to force exit it.
If I remove the last added keyword and links, it runs but not correctly and prints that link is not in list. Similar function findKeyword does the same - prints that keyword not in list.
How can I fix the code so it runs for any list?
Code:
NodeT *addNewKeyword(NodeT **list, char *keyword) {
NodeT *new = malloc(sizeof(NodeT));
assert(new != NULL);
strcpy(new->keyword, keyword);
new->next = *list;
*list = new;
return 0;
}
LinkT *addNewLink(NodeT *list, char *link) {
LinkT *new = malloc(sizeof(LinkT));
assert(new != NULL);
strcpy(new->link, link);
new->next = list->links;
list->links = new;
return 0;
}
NodeT *findKeyword(NodeT *list, char *keyword) {
NodeT *current = list;
while (current != NULL) {
if (strcmp(current->keyword, keyword) == 0) {
return current;
}
current = current->next;
}
return NULL;
}
void *findLink (NodeT *list, char *keyword, char *link) {
NodeT *current = list;
while (current != NULL) {
if (strcmp(current->keyword, keyword) == 0) {
LinkT *currentLink = list->links;
while (currentLink != NULL) {
if (strcmp(currentLink->link, link) == 0) {
return currentLink;
}
currentLink = list->links->next;
}
return NULL;
}
current = current->next;
}
return NULL;
}
void *printList(NodeT *listNode) {
while (listNode != NULL) {
printf("%s\n", listNode->keyword);
LinkT *listLink = listNode->links;
if (listLink == NULL) {
printf("List is empty.\n");
} else {
while (listLink != NULL) {
printf(" %s\n", listLink->url);
listLink = listLink->next;
}
}
listNode = listNode->next;
}
return 0;
}
int main() {
NodeT *wordsList = NULL;
addNewKeyword(&wordsList, "Moon");
addNewLink(wordsList, "Link-1");
addNewLink(wordsList, "Link-3");
addNewKeyword(&wordsList, "Earth");
addNewLink(wordsList, "Link-2");
/* runs after removing following lines: */
// addNewKeyword(&wordsList, "Asteroid");
// addNewLink(wordsList, "Link-23");
// addNewLink(wordsList, "Link-45");
if (findKeyword(wordsList, "Moon") == 0) {
printf("Keyword is in list.\n");
} else {
printf("Keyword is not in list.\n");
}
if (findLink(wordsList, "Moon", "Link-1") == NULL) {
printf("Link is not in list.\n");
addNewLink(wordsList, "Link-1");
} else {
printf("Link is in list.\n");
}
printList(wordsList);
return 0;
}
EDIT 1
I'm returning a node from a function type void, which might be the reason to don't work as expected. What type is the findLink function needs to be?
EDIT 2
As was pointed out in answers by Sahin, I updated the findLink function and now the program runs, but adding an element to incorrect place. Change in main code:
int main() {
...
addNewKeyword(&wordsList, "Moon");
addNewLink(wordsList, "Link-1");
addNewLink(wordsList, "Link-3");
addNewKeyword(&wordsList, "Earth");
addNewLink(wordsList, "Link-2");
addNewKeyword(&wordsList, "Asteroid");
addNewLink(wordsList, "Link-23");
addNewLink(wordsList, "Link-45");
if (findLink(wordsList, "Earth", "Link-4") == NULL) {
printf("Link is not in list.\n");
addNewLink(wordsList, "Link-4");
} else {
printf("Link is in list.\n");
}
...
}
Output:
Link is not in list.
Asteroid
Link-4
Link-45
Link-23
Earth
Link-2
Moon
Link-3
Link-1
void *findLink (NodeT *list, char *keyword, char *link) {
NodeT *current = list;
while (current != NULL) {
if (strcmp(current->keyword, keyword) == 0) {
LinkT *currentLink = current->links;//An error is here, you always look for the same list.
while (currentLink != NULL) {
if (strcmp(currentLink->link, link) == 0) {
return currentLink;
}
currentLink = currentLink->next;//The other error is here
}
return NULL;
}
current = current->next;
}
return NULL;
}
I guess your error is here, you need to get the next of the currentLink, since the next of the same node will always be the same. You need to test the code after doing these corrections, then we can look the results again.
EDIT:
In response to edit 2:
NodeT *addNewKeyword(NodeT **list, char *keyword) {
NodeT *new = malloc(sizeof(NodeT));
assert(new != NULL);
strcpy(new->keyword, keyword);
new->next = *list;
*list = new; //This row
return 0;
}
In the row that I put a comment, you change the value inside the parameter and the value in the list variable changes. Since you call addNeKeyword with "Asteroid" at last, the new Links are added to there. So, please specify more details. What do you really want?
I'm making a generic map in c (using clion) and in my nodeDestroy function I call a generic freeing function to free the key and data of the node list.
nodeDestroy:
void nodeDestroy(Node node) {
Node current = node;
Node next;
while (current != NULL) {
next = current->next;
current->free_key(current->key);
current->key = NULL;
current->free_data(current->data);
current->data = NULL;
free(current);
current = next;
}
node = NULL;
}
this is the node struct:
struct node_t{
MapKeyElement key;
MapDataElement data;
Node next;
copyMapKeyElements copy_key;
copyMapDataElements copy_data;
freeMapKeyElements free_key;
freeMapDataElements free_data;
};
I am testing the map with integer pointers (the data and key of each node are int*) and this is the integer freeing function:
static void freeInt(MapKeyElement e) {
free((int*)e);
}
free_key and free_data are given to me in nodeCreate and both are set to freeInt. When I check this in valgrind the key and data of each node aren't freed. any idea?
So I noticed the problem occurs only when using another function: mapPut. I'll add implementation of functions required to understand the code and some things I didn't add before.
/** Data element data type for map container */
typedef void* MapDataElement;
/** Key element data type for map container */
typedef void* MapKeyElement;
/** Type of function for copying a data element of the map */
typedef MapDataElement(*copyMapDataElements)(MapDataElement);
/** Type of function for copying a key element of the map */
typedef MapKeyElement(*copyMapKeyElements)(MapKeyElement);
/** Type of function for deallocating a data element of the map */
typedef void(*freeMapDataElements)(MapDataElement);
/** Type of function for deallocating a key element of the map */
typedef void(*freeMapKeyElements)(MapKeyElement);
struct Map_t{
Node iterator;
copyMapKeyElements copy_key;
copyMapDataElements copy_data;
freeMapKeyElements free_key;
freeMapDataElements free_data;
compareMapKeyElements compare_keys;
Node head;
};
bool mapContains(Map map, MapKeyElement element){
if (map == NULL || element == NULL){
return false;
}
mapGetFirst(map);
if (getKey(map->iterator) != NULL && map->compare_keys(getKey(map->iterator)
, element) == 0){
return true;
}
while (mapGetNext(map) != NULL){
if (getKey(map->iterator) != NULL &&
map->compare_keys(getKey(map->iterator), element) == 0){
return true;
}
}
return false;
}
MapResult mapPut(Map map, MapKeyElement keyElement, MapDataElement dataElement){
if (map == NULL || keyElement == NULL || dataElement == NULL){
return MAP_NULL_ARGUMENT;
}
MapDataElement data_copy = map->copy_data(dataElement);
if (data_copy == NULL){
return MAP_OUT_OF_MEMORY;
}
MapResult result = MAP_SUCCESS;
if (mapContains(map,keyElement) == true){
mapGetFirst(map);
while (map->iterator != NULL){
if (map->compare_keys(keyElement, getKey(map->iterator)) == 0) {
map->free_data(getData(map->iterator));
setData(map->iterator, data_copy);
return setIterator(map, MAP_SUCCESS);
}
map->iterator = getNext(map->iterator);
}
}
mapGetFirst(map);
MapKeyElement key_copy = map->copy_key(keyElement);
if (key_copy == NULL){
map->free_data(data_copy);
return setIterator(map, MAP_OUT_OF_MEMORY);
}
if (getKey(map->iterator) == NULL){
setKey(map->iterator, key_copy);
setData(map->iterator, data_copy);
return setIterator(map, MAP_SUCCESS);
}
Node new_node = nodeCreate(map->copy_data, map->copy_key,
map->free_data, map->free_key, &result);
if (result == MAP_OUT_OF_MEMORY) {
map->free_data(data_copy);
map->free_key(key_copy);
return setIterator(map, MAP_OUT_OF_MEMORY);
}
setData(new_node, data_copy);
setKey(new_node, key_copy);
if (map->compare_keys(key_copy, getKey(map->iterator)) < 0){
setNext(new_node,map->iterator);
map->head = new_node;
return setIterator(map, MAP_SUCCESS);
}
while(map->iterator != NULL) {
if (getNext(map->iterator) == NULL){
setNext(map->iterator, new_node);
break;
}
if (map->compare_keys(key_copy, getKey(getNext(map->iterator))) < 0){
Node temp_next = getNext(map->iterator);
setNext(map->iterator, new_node);
setNext(new_node, temp_next);
break;
}
map->iterator = getNext(map->iterator);
}
return setIterator(map, MAP_SUCCESS);
}
static MapResult setIterator(Map map, MapResult result){
map->iterator = NULL;
return result;
}
MapKeyElement mapGetFirst(Map map){
if (map == NULL){
return NULL;
}
map->iterator = map->head;
return getKey(map->head);
}
I am using a struct like this
struct infoM {
char* direction;
int key;
};
typedef struct nodeM{
struct infoM nodeInfo;
struct nodeM *next;
struct nodeM *prev;
} node;
typedef node list;
I have one function that returns the wanted node by a specific field
node * search(list *l, char* direction) {}
And this is my function to remove elements from the list
int delete(list *l, char* direction) {
node *tmp = search(l, direction);
if (tmp != NULL) {
node *ant = tmp->prev;
node *seg = tmp->next;
if (seg != NULL) {
if (ant != NULL) {
ant->next = seg;
seg->prev = ant;
free(tmp);
return 1;
} else { //prev null
seg->prev = NULL;
*l = *seg;
tmp = NULL;
free(tmp);
return 1;
}
} else { //next null
if (ant == NULL) {
l->nodeInfo.key = somevalue;
l->next = NULL;
l->prev = NULL;
return 1;
} else {
printf("Here is the problem\n");
ant->next = NULL;
free(tmp);
return 1;
}
}
} else { //tmp nulo
perror("Error delete : node null\n");
return 0;
}
}
If I have 4 elements in the list, 1234 and I delete first first element everything is okay and returns 234. If I delete the last element it returns 23 seems to work great. But if I try to delete the last element now the function does nothing despite being the same case that when it is 234 and I don't understand why. The list is not being updated.
In the main I am using the list like this :
list a;
delete(&a, "whatever");
What am I doing wrong ?
This is the code for search
node * createnode(){
node *tmp = (node *) malloc (sizeof(node));
return tmp;
}
node * search(list *l, char* direction) {
node *tmp = createnode();
if (l->nodeInfo.key == 777) {
perror("Error search: empty list\n");
return NULL;
}
tmp=l;
while((strcmp(direction, tmp->nodeInfo.direction) !=0) && (tmp->next != NULL)) {
tmp = tmp->next;
}
if (strcmp(direction, tmp->nodeInfo.direction) == 0) {
return tmp;
} else {
perror("Error search: element not found\n");
return NULL;
}
}
In this video (a section/recitation for an online course called CS50), around 1h00m00s, the student instructor goes into pointers-to-pointers and why it is more efficient useful to implement insert on the binary tree this way. At least, that is what I'm getting from the argument.
I did a recursive implementation both ways. I don't see why Option A is better than Option B in the below...perhaps you could help me reason this out or point me in the right direction if I'm misunderstood?
Option A (with pointers-to-pointers)
bool insert(int value, node* tree)
{
node** tmptree = &tree;
// make sure the tree itself isn't null
if(*tmptree != NULL)
{
if(value == (*tmptree)->value)
{
return false;
}
else if(value < (*tmptree)->value)
{
tmptree = &(*tmptree)->left;
// we must be at a null leaf!
if(*tmptree == NULL)
{
// make sure we built a leaf
*tmptree = build_node(value);
if(*tmptree == NULL)
{
return false;
}
return true;
}
else
{
return insert(value, *tmptree);
}
}
else
{
tmptree = &(*tmptree)->right;
if(*tmptree == NULL)
{
*tmptree = build_node(value);
if(*tmptree == NULL)
{
return false;
}
return true;
}
else
{
return insert(value, *tmptree);
}
}
}
return false; // if the tree is null
}
Option B (regular ptrs)
bool insert(int value, node* tree)
{
if(tree != NULL)
{
if(value == tree->value)
{
return false;
}
else if(value < tree->value)
{
if(tree->left == NULL)
{
node* tmp = build_node(value);
if(tmp != NULL)
{
tree->left = tmp;
return true;
}
return false;
}
else
{
return insert(value, tree->left);
}
}
else
{
if(tree->right == NULL)
{
node* tmp = build_node(value);
if(tmp != NULL)
{
tree->right = tmp;
return true;
}
return false;
}
else
{
return insert(value, tree->right);
}
}
}
return false; // if the tree is null
}
The build_node function:
node* build_node(int value)
{
node* node = malloc(sizeof( node ));
if(node == NULL)
return NULL;
node->value = value;
node->left = NULL;
node->right = NULL;
return node;
}
I believe you have misunderstood why there is a pointer-to-pointer in the original code. The "Option a" doesn't make any sense, there is no advantage of using pointer-to-pointer just for the sake of it.
The only reason you would use pointer-to-pointer is because you want to change the pointed-to address and return it to the caller.
For example
void func (int** ptr)
{
*ptr = something;
}
func(&my_ptr);
// is the very thing same as
int* func (int* ptr)
{
return something;
}
my_ptr = func(my_ptr);
You can't use the second version and type ptr = something inside the function, because ptr is a local variable and will cease to exist once you leave the function. Assigning something to it won't affect the original pointer at the caller's side.
The only advantage of the pointer-to-pointer version is that the returned type can be used for something else, like for example returning an error code.
And that seems to be exactly why the guy in that video used it. His function looks like
bool insert (int value, node** tree);
where the *tree is assigned to point at another address from inside the function and the bool is used as a status code, when he reaches the end of a tree branch.