/* ELAPI Different serialization methods of the collection. Copyright (C) Dmitri Pal 2009 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include "elapi_collection.h" #include "elapi_debug.h" #include "elapi_util.h" #ifndef MAX #define MAX(a,b) a > b ? a : b #endif /* Internal defines used in different places */ #define UNKNOWN "" #define UNKNOWN_LEN sizeof(UNKNOWN)-1 #define BAD_FORMAT "" #define BAD_FORMAT_LEN sizeof(BAD_FORMAT)-1 #define FMT_STRING 0 #define FMT_INTEGER 1 #define FMT_UNSIGNED 2 #define FMT_LONG 3 #define FMT_ULONG 4 #define FMT_DOUBLE 5 #define TIMESTAMP "(" TS_NAME ")" #define TIMESTAMP_LEN sizeof(TS_NAME) + 1 /* Return a static string based on type of the element */ const char *get_type(int type) { switch(type) { case ELAPI_TYPE_STRING: return ELAPI_TYPE_NAME_STRING; case ELAPI_TYPE_INTEGER: return ELAPI_TYPE_NAME_INTEGER; case ELAPI_TYPE_UNSIGNED: return ELAPI_TYPE_NAME_UNSIGNED; case ELAPI_TYPE_LONG: return ELAPI_TYPE_NAME_LONG; case ELAPI_TYPE_ULONG: return ELAPI_TYPE_NAME_ULONG; case ELAPI_TYPE_BINARY: return ELAPI_TYPE_NAME_BINARY; default: return ELAPI_TYPE_NAME_UNKNOWN; } } /* Calculate the potential size of the item */ int get_data_len(int type, int length) { int len = 0; DEBUG_STRING("util_get_item_len","Entry point"); switch(type) { case ELAPI_TYPE_INTEGER: case ELAPI_TYPE_UNSIGNED: case ELAPI_TYPE_LONG: case ELAPI_TYPE_ULONG: len = 15; break; case ELAPI_TYPE_STRING: case ELAPI_TYPE_BINARY: len = length * 2 + 2; break; case ELAPI_TYPE_DOUBLE: len = 64; break; default: len = 0; break; } DEBUG_STRING("util_get_item_len","Exit point"); return len; } /* Copy data escaping characters */ static int copy_esc(char *dest,char *source,char esc) { int i=0; int j=0; *(dest +j) = esc; j++; while(*(source+i) != '\0') { if((*(source+i) == '\\') || (*(source+i) == esc)) { *(dest +j) = '\\'; j++; } *(dest +j) = *(source +i); i++; j++; } *(dest +j) = esc; j++; return j; } /* Grow buffer to accomodate more space */ int grow_buffer(struct serial_data *buf_data, int len) { void *tmp; DEBUG_STRING("grow_buffer","Entry point"); DEBUG_NUMBER("Current length: ",buf_data->length); DEBUG_NUMBER("Increment length: ",len); DEBUG_NUMBER("Expected length: ",buf_data->length+len); DEBUG_NUMBER("Current size: ",buf_data->size); /* Grow buffer if needed */ while(buf_data->length+len >= buf_data->size) { errno = 0; tmp = realloc(buf_data->buffer,buf_data->size+BLOCK_SIZE); if(tmp == NULL) { DEBUG_NUMBER("Error. Failed to allocate memory. Errno: ",errno); return errno; } buf_data->buffer = (char *)(tmp); buf_data->size += BLOCK_SIZE; DEBUG_NUMBER("New size: ",buf_data->size); } DEBUG_NUMBER("Final size: ",buf_data->size); DEBUG_STRING("grow_buffer","Success Exit."); return EOK; } /* Specail function to add different formatting symbols to the output */ int put_marker(struct serial_data *buf_data, void *data, int len) { int error = EOK; DEBUG_STRING("put_marker","Entry point"); DEBUG_NUMBER("Marker length: ",len); error = grow_buffer(buf_data, len); if(error) { DEBUG_NUMBER("grow_buffer failed with: ",error); return error; } memcpy(buf_data->buffer+buf_data->length,data,len); buf_data->length+=len; *(buf_data->buffer+buf_data->length) = '\0'; DEBUG_STRING("put_marker","Success exit"); return error; } /* Add item's data */ int serialize(char *property_in, int property_len_in, int type, void *data_in, int length_in, void *custom_data, int *dummy) { int len; struct serial_data *buf_data; char *property; void *data; int property_len; int length; int error = EOK; int i; DEBUG_STRING("serialize","Entry point"); *dummy = 0; /* Check is there is buffer. If not allocate */ buf_data = (struct serial_data *)(custom_data); if(buf_data == (struct serial_data *)(NULL)) { DEBUG_STRING("Error.","Storage data is not passed in!"); return EINVAL; } if(buf_data->buffer == NULL) { DEBUG_STRING("First time use.","Allocating buffer."); errno = 0; buf_data->buffer = malloc(BLOCK_SIZE); if(buf_data->buffer == NULL) { DEBUG_NUMBER("Error. Failed to allocate memory. Errno: ",errno); return errno; } *(buf_data->buffer)='\0'; buf_data->length=0; buf_data->size = BLOCK_SIZE; } DEBUG_NUMBER("Buffer len: ", buf_data->length); DEBUG_NUMBER("Buffer size: ", buf_data->size); DEBUG_STRING("Buffer: ", buf_data->buffer); /* Check the beginning of the collection */ if(type == ELAPI_TYPE_COLLECTION) { DEBUG_STRING("Serializing collection: ", property_in); DEBUG_STRING("First header. ", ""); if((error=put_marker(buf_data,"(",1))) return error; property = TEXT_COLLECTION; property_len = TEXT_COLLEN; data = property_in; length = property_len_in+1; type=ELAPI_TYPE_STRING; buf_data->nest_level++; } /* Check for subcollections */ else if(type==ELAPI_TYPE_COLLECTIONREF) { /* Skip */ return EOK; } /* Check for the end of the collection */ else if(type==ELAPI_TYPE_END) { if((buf_data->length>0) && (*(buf_data->buffer+buf_data->length-1) == ',')) { buf_data->length--; *(buf_data->buffer+buf_data->length) = '\0'; } if(buf_data->nest_level>0) { buf_data->nest_level--; if((error=put_marker(buf_data,")",1))) return error; } return EOK; } else { property = property_in; property_len = property_len_in; data = data_in; length = length_in; } DEBUG_STRING("Property: ", property); DEBUG_NUMBER("Property length: ", property_len); /* Start with property and "=" */ if((error=put_marker(buf_data,property,property_len)) || (error=put_marker(buf_data,"=",1))) return error; /* Get projected length of the item */ len = get_data_len(type,length); DEBUG_NUMBER("Expected data length: ",len); DEBUG_STRING("Buffer so far: ", buf_data->buffer); /* Make sure we have enough space */ if((error=grow_buffer(buf_data,len))) return error; /* Add the value */ switch(type) { case ELAPI_TYPE_STRING: /* No escaping for now */ len = copy_esc(buf_data->buffer+buf_data->length,(char *)(data),'"'); break; case ELAPI_TYPE_BINARY: *(buf_data->buffer+buf_data->length) = '\''; for(i=0;ibuffer+buf_data->length+i*2 + 1,"%02X",*((unsigned char *)(data+i))); len = length * 2 + 1; *(buf_data->buffer+buf_data->length + len) = '\''; len++; break; case ELAPI_TYPE_INTEGER: len = sprintf(buf_data->buffer+buf_data->length,"%d",*((int *)(data))); break; case ELAPI_TYPE_UNSIGNED: len = sprintf(buf_data->buffer+buf_data->length,"%u",*((unsigned int *)(data))); break; case ELAPI_TYPE_LONG: len = sprintf(buf_data->buffer+buf_data->length,"%ld",*((long *)(data))); break; case ELAPI_TYPE_ULONG: len = sprintf(buf_data->buffer+buf_data->length,"%lu",*((unsigned long *)(data))); break; case ELAPI_TYPE_DOUBLE: len = sprintf(buf_data->buffer+buf_data->length,"%.4f",*((double *)(data))); break; default: *(buf_data->buffer+buf_data->length) = '\0'; len = 0; break; } /* Adjust length */ buf_data->length+=len; *(buf_data->buffer+buf_data->length) = '\0'; /* Always put a comma at the end */ if((error=put_marker(buf_data,",",1))) return error; DEBUG_STRING("Data: ",buf_data->buffer); DEBUG_STRING("serialize","Exit point"); return EOK; } /* Add item's data */ int xml_add(char *property, int property_len, int type, void *data, int length, void *custom_data, int *dummy) { int rc; struct xml_data *buf_data; int error = EOK; char *data_xml; char *name; DEBUG_STRING("xml_add","Entry point"); *dummy = 0; /* Check is there is buffer. If not allocate */ buf_data = (struct xml_data *)(custom_data); if(buf_data == (struct xml_data *)(NULL)) { DEBUG_STRING("Error.","Storage data is not passed in!"); return EINVAL; } /* Check if there is buffer allocated */ if(buf_data->buf == (xmlBufferPtr)(NULL)) { DEBUG_STRING("xml_add", "First use - allocating memory"); buf_data->buf = xmlBufferCreate(); if (buf_data->buf == NULL) { DEBUG_STRING("xml_add", "Error creating the xml buffer"); return ENOMEM; } DEBUG_NUMBER("Buffer allocated", buf_data->buf); DEBUG_NUMBER("Buffer output", (buf_data->buf)->content); /* Create a new XmlWriter for memory, with no compression. * Remark: there is no compression for this kind of xmlTextWriter */ buf_data->writer = xmlNewTextWriterMemory(buf_data->buf, 0); if (buf_data->writer == NULL) { error = errno; DEBUG_STRING("xml_add", "Error creating the xml writer"); xmlBufferFree(buf_data->buf); buf_data->buf = (xmlBufferPtr)(NULL); return ENOMEM; } /* Start the document with the xml default for the version, * encoding and the default for the standalone declaration. */ rc = xmlTextWriterStartDocument(buf_data->writer, NULL, NULL, NULL); if (rc < 0) { DEBUG_STRING("xml_add", "Error at xmlTextWriterStartDocument"); xmlFreeTextWriter(buf_data->writer); xmlBufferFree(buf_data->buf); buf_data->writer = (xmlTextWriterPtr)(NULL); buf_data->buf = (xmlBufferPtr)(NULL); return EIO; } buf_data->given_name = NULL; buf_data->level = 0; } DEBUG_STRING("current buffer:", (buf_data->buf)->content); DEBUG_NUMBER("Writer", buf_data->writer); /* Check the beginning of the collection */ if(type == ELAPI_TYPE_COLLECTION) { DEBUG_STRING("XML collection start: ", property); rc = xmlTextWriterStartElement(buf_data->writer, BAD_CAST ELEMENT_COLLECTION); if (rc < 0) { DEBUG_STRING("xml_add", "Error at xmlTextWriterStartElement"); xmlFreeTextWriter(buf_data->writer); xmlBufferFree(buf_data->buf); buf_data->writer = (xmlTextWriterPtr)(NULL); buf_data->buf = (xmlBufferPtr)(NULL); return EIO; } if(buf_data->given_name != NULL) name = buf_data->given_name; else name = property; rc = xmlTextWriterWriteAttribute(buf_data->writer, BAD_CAST ATTRIBUTE_NAME, BAD_CAST property); if (rc < 0) { DEBUG_STRING("xml_add", "Error at xmlTextWriterWriteAttribute"); xmlFreeTextWriter(buf_data->writer); xmlBufferFree(buf_data->buf); buf_data->writer = (xmlTextWriterPtr)(NULL); buf_data->buf = (xmlBufferPtr)(NULL); return EIO; } /* Make sure we track the level */ buf_data->level++; } /* Check for subcollections */ else if(type==ELAPI_TYPE_COLLECTIONREF) { buf_data->given_name = property; } /* Check for the end of the collection */ else if(type==ELAPI_TYPE_END) { buf_data->given_name = NULL; buf_data->level--; /* Check if this is the end of the whole collection */ if(buf_data->level == 0) { rc = xmlTextWriterEndDocument(buf_data->writer); if (rc < 0) { DEBUG_STRING("xml_add", "Error at xmlTextWriterEndDocument"); xmlFreeTextWriter(buf_data->writer); xmlBufferFree(buf_data->buf); buf_data->writer = (xmlTextWriterPtr)(NULL); buf_data->buf = (xmlBufferPtr)(NULL); return EIO; } } else { rc = xmlTextWriterFullEndElement(buf_data->writer); if (rc < 0) { DEBUG_STRING("xml_add", "Error at xmlTextWriterEndElement"); xmlFreeTextWriter(buf_data->writer); xmlBufferFree(buf_data->buf); buf_data->writer = (xmlTextWriterPtr)(NULL); buf_data->buf = (xmlBufferPtr)(NULL); return EIO; } } } else { DEBUG_STRING("Property: ", property); DEBUG_NUMBER("Property length: ", property_len); rc = xmlTextWriterStartElement(buf_data->writer, BAD_CAST ELEMENT_MEMBER); if (rc < 0) { DEBUG_STRING("xml_add", "Error at xmlTextWriterStartElement"); xmlFreeTextWriter(buf_data->writer); xmlBufferFree(buf_data->buf); buf_data->writer = (xmlTextWriterPtr)(NULL); buf_data->buf = (xmlBufferPtr)(NULL); return EIO; } rc = xmlTextWriterWriteAttribute(buf_data->writer, BAD_CAST ATTRIBUTE_NAME, BAD_CAST property); if (rc < 0) { DEBUG_STRING("xml_add", "Error at xmlTextWriterWriteAttribute"); xmlFreeTextWriter(buf_data->writer); xmlBufferFree(buf_data->buf); buf_data->writer = (xmlTextWriterPtr)(NULL); buf_data->buf = (xmlBufferPtr)(NULL); return EIO; } rc = xmlTextWriterWriteAttribute(buf_data->writer, BAD_CAST ATTRIBUTE_TYPE, BAD_CAST get_type(type)); if (rc < 0) { DEBUG_STRING("xml_add", "Error at xmlTextWriterWriteAttribute"); xmlFreeTextWriter(buf_data->writer); xmlBufferFree(buf_data->buf); buf_data->writer = (xmlTextWriterPtr)(NULL); buf_data->buf = (xmlBufferPtr)(NULL); return EIO; } /* Add the value */ switch(type) { case ELAPI_TYPE_STRING: data_xml = (char *)(data); if(!xmlCheckUTF8((const unsigned char *)(data_xml))) data_xml = BAD_DATA; rc = xmlTextWriterWriteString(buf_data->writer, BAD_CAST data_xml); break; case ELAPI_TYPE_BINARY: rc = xmlTextWriterWriteBase64(buf_data->writer, (const char *)(data),0,length); break; case ELAPI_TYPE_INTEGER: rc = xmlTextWriterWriteFormatString(buf_data->writer, "%d", *((int *)(data))); break; case ELAPI_TYPE_UNSIGNED: rc = xmlTextWriterWriteFormatString(buf_data->writer, "%u", *((unsigned int *)(data))); break; case ELAPI_TYPE_LONG: rc = xmlTextWriterWriteFormatString(buf_data->writer, "%ld", *((long *)(data))); break; case ELAPI_TYPE_ULONG: rc = xmlTextWriterWriteFormatString(buf_data->writer, "%lu", *((unsigned long *)(data))); break; case ELAPI_TYPE_DOUBLE: rc = xmlTextWriterWriteFormatString(buf_data->writer, "%.4f", *((double *)(data))); break; default: rc = xmlTextWriterWriteString(buf_data->writer, BAD_CAST ""); break; } if (rc < 0) { DEBUG_STRING("xml_add", "Error trying put data into XML"); xmlFreeTextWriter(buf_data->writer); xmlBufferFree(buf_data->buf); buf_data->writer = (xmlTextWriterPtr)(NULL); buf_data->buf = (xmlBufferPtr)(NULL); return EIO; } rc = xmlTextWriterFullEndElement(buf_data->writer); if (rc < 0) { DEBUG_STRING("xml_add", "Error at xmlTextWriterFullEndElement"); xmlFreeTextWriter(buf_data->writer); xmlBufferFree(buf_data->buf); buf_data->writer = (xmlTextWriterPtr)(NULL); buf_data->buf = (xmlBufferPtr)(NULL); return EIO; } } DEBUG_STRING("xml_add","Exit point"); return EOK; } /* Extracet and lookup item */ static int extract_item(char *start, struct collection_item *event, struct collection_item **item, int *index) { char *end; int error = EOK; DEBUG_STRING("extract_item","Entry point"); DEBUG_STRING("Start:",start); start++; end = start; while((*end != ')') && (*end != '\0')) end++; if(*end == '\0') return EINVAL; DEBUG_STRING("End:",end); DEBUG_NUMBER("Length:",end-start); *end = '\0'; error = get_item(event,start,ELAPI_TYPE_ANY, ELAPI_TRAVERSE_DEFAULT,item); *end = ')'; *index += (end-start)+1; DEBUG_STRING("extract_item","Exit point"); return error; } /* Function to serialize one item using provided format if any */ int sprintf_item(struct serial_data *data, struct collection_item *item, char *one_format) { int len; int length; int error = EOK; int i; int len_format = 0; char *formats[] = {"%s","%d","%u","%ld","%lu","%.4f"}; char *fmt; int block; char *start; DEBUG_STRING("sprintf_item","Entry point"); DEBUG_NUMBER("Buffer len: ", data->length); DEBUG_NUMBER("Buffer size: ", data->size); DEBUG_STRING("Buffer: ", data->buffer); DEBUG_STRING("Using format: ", one_format); if(one_format!=NULL) len_format = strlen(one_format); /* Handle special binary case - ignore format for it */ if(item->type == ELAPI_TYPE_BINARY) { /* Bake sure we have enough memory */ if((error=grow_buffer(data,item->length * 2 + 2))) return error; /* Put opening quote */ *(data->buffer+data->length) = '\''; data->length++; for(i=0;ilength;i++) sprintf(data->buffer+data->length+i*2,"%02X",*((unsigned char *)(item->data+i))); data->length+=item->length*2; *(data->buffer+data->length) = '\''; data->length++; *(data->buffer+data->length) = '\0'; } else { /* For other types use a version of sprintf, but determine format first */ switch(item->type) { case ELAPI_TYPE_STRING: if((len_format > 0) && (*(one_format+len_format-1) == 's')) fmt=one_format; else fmt = formats[FMT_STRING]; break; case ELAPI_TYPE_INTEGER: if((len_format > 0) && (*(one_format+len_format-1) != 's')) fmt=one_format; else fmt = formats[FMT_INTEGER]; break; case ELAPI_TYPE_UNSIGNED: if((len_format > 0) && (*(one_format+len_format-1) != 's')) fmt=one_format; else fmt = formats[FMT_UNSIGNED]; break; case ELAPI_TYPE_LONG: if((len_format > 0) && (*(one_format+len_format-1) != 's')) fmt=one_format; else fmt = formats[FMT_LONG]; break; case ELAPI_TYPE_ULONG: if((len_format > 0) && (*(one_format+len_format-1) != 's')) fmt=one_format; else fmt = formats[FMT_ULONG]; break; case ELAPI_TYPE_DOUBLE: if((len_format > 0) && (*(one_format+len_format-1) != 's') && (*(one_format+len_format-1) != 'c')) fmt=one_format; else fmt = formats[FMT_INTEGER]; break; default: /* In case we do not know the type */ error = put_marker(data,UNKNOWN,UNKNOWN_LEN); if(error) { DEBUG_NUMBER("put_marker returned error:",error); return error; } error = put_marker(data,item->property,item->property_len); if(error) { DEBUG_NUMBER("put_marker returned error:",error); return error; } DEBUG_STRING("sprintf_item","Unknown item exit point"); return EOK; } start = data->buffer+data->length; block = data->size-data->length-1; while (1) { switch(item->type) { case ELAPI_TYPE_STRING: len = snprintf(start,block, fmt, (char *)(item->data)); break; case ELAPI_TYPE_INTEGER: len = snprintf(start,block, fmt, *((int *)(item->data))); break; case ELAPI_TYPE_UNSIGNED: len = snprintf(start,block, fmt, *((unsigned *)(item->data))); break; case ELAPI_TYPE_LONG: len = snprintf(start,block, fmt, *((long *)(item->data))); break; case ELAPI_TYPE_ULONG: len = snprintf(start,block, fmt, *((unsigned long *)(item->data))); break; case ELAPI_TYPE_DOUBLE: len = snprintf(start,block, fmt, *((double *)(item->data))); break; default: /* Not possible */ DEBUG_STRING("sprintf_item","Unknown item exit point 2."); return EOK; } DEBUG_NUMBER("snprintf returned:",len); /* Did it fit? */ if (len > -1 && len < block) break; /* Else try again with more space. Based on printf example. */ if (len > -1) block = MAX(len+1,BLOCK_SIZE); else block = MAX(block *2,BLOCK_SIZE); DEBUG_NUMBER("Need to grow by:",block); if((error=grow_buffer(data,block))) return error; } /* Adjust length */ data->length+=len; *(data->buffer+data->length) = '\0'; } DEBUG_STRING("Data: ",data->buffer); DEBUG_STRING("sprintf_item","Exit point"); return EOK; } /* If time is missing add it */ static int add_time(struct serial_data *data, struct collection_item *event) { struct collection_item *timestamp = (struct collection_item *)(NULL); int error = EOK; DEBUG_STRING("add_time","Entry point"); error = set_timestamp(event,×tamp,NULL); if(error) { DEBUG_NUMBER("set_timestamp returned error:",error); return error; } error = put_marker(data,timestamp->data,timestamp->length-1); if(error) { DEBUG_NUMBER("put_marker returned error:",error); return error; } DEBUG_STRING("add_time","Exit point"); } /* Internal function to parse out item and put the item data based on the lookup */ static int process_item(struct serial_data *data, char *format_str, int *index, struct collection_item *event, char *one_format, int *brk) { char *start; char *end; int block; struct collection_item *item = (struct collection_item *)(NULL); int error = EOK; DEBUG_STRING("process_item","Entry point"); start = format_str + *index; error = extract_item(start,event,&item,index); if(error) { /* This is a problem with format not with memory */ error = put_marker(data,BAD_FORMAT,BAD_FORMAT_LEN); if(error) { DEBUG_NUMBER("put_marker bad format returned error:",error); return error; } /* Bad format - we are done */ *brk = 1; } else { /* Check if item exists */ if(item == (struct collection_item *)(NULL)) { if(strncmp(start,TIMESTAMP,TIMESTAMP_LEN) == 0) { DEBUG_STRING("process_item","Adding time"); /* Add a timestamp automatically */ error = add_time(data,event); } else { /* We will put placeholder instead */ error = put_marker(data,UNKNOWN,UNKNOWN_LEN); if(error) { DEBUG_NUMBER("put_marker returned error:",error); return error; } /* Then put the piece we could not find */ error = put_marker(data,start+1,*index-(start-format_str)-1); } if(error) { DEBUG_NUMBER("put_marker/add_time returned error:",error); return error; } (*index)++; DEBUG_STRING("Format after processing not found item:",format_str+*index); } else { /* Item found - call sprintf_item using native format ! */ error = sprintf_item(data,item,one_format); if(error) { DEBUG_NUMBER("sprintf_item returned error:",error); return error; } (*index)++; DEBUG_STRING("Format after processing item:",format_str+*index); } } DEBUG_STRING("process_item","Exit"); return EOK; } #define CHECK_VALID(x) ((x == 'd')||(x == 'i')||(x == 'o')||(x == 'u')||\ (x == 'x')||(x == 'X')||(x == 'c')||(x == 's')||\ (x == 'e')||(x == 'E')||(x == 'g')||(x == 'G')||\ (x == 'f')||(x == 'F')||(x == 'a')||(x == 'A')) /* Serialize using format */ int serialize_with_format(struct collection_item *event, char *format_str, struct serial_data *data) { int i=0; char *one_format; int brk; char *start; char *end; int block; int error = EOK; int len; char *dup; DEBUG_STRING("serialize_with_format","Entry point"); /* Allocate output buffer */ errno = 0; data->buffer = malloc(BLOCK_SIZE); if(data->buffer == NULL) { DEBUG_NUMBER("Out of memory",errno); return ENOMEM; } data->size = BLOCK_SIZE; data->length = 0; /* Create a copy so we can change it if needed */ len = strlen(format_str); errno = 0; dup = malloc(len+1); if(dup == NULL) { DEBUG_NUMBER("Out of memory",errno); free(data->buffer); data->buffer=NULL; return ENOMEM; } memcpy(dup,format_str,len+1); /* Create buffer for format specifier */ errno = 0; one_format = malloc(len+1); if(one_format == NULL) { DEBUG_NUMBER("Out of memory",errno); free(data->buffer); free(dup); data->buffer=NULL; return ENOMEM; } while(1) { /* Copy characters directly into output */ start = dup+i; end = start; block = 0; while((*end != '%') && (*end != '\0')) end++; if(end > start) { /* We have a block to copy to output buffer */ block = end-start; error = put_marker(data,start,block); if(error) { DEBUG_NUMBER("put_marker returned error:",error); free(data->buffer); data->buffer=NULL; free(one_format); free(dup); return ENOMEM; } } /* Check if we are done */ if(*end == '\0') break; /* We are not done - we have %*/ i+=block+1; /* Here we are at the beginning of the string after % */ DEBUG_STRING("Format after %:",dup+i); /* Handle special case */ if(*(dup+i) == '%') { error = put_marker(data,"%",1); if(error) { DEBUG_NUMBER("put_marker returned error:",error); free(data->buffer); data->buffer=NULL; free(one_format); free(dup); return error; } i++; DEBUG_STRING("Format after processing %%:",dup+i); continue; } /* We are here in case there is a real format token */ if(*(dup+i) == '(') { /* This is our special case when we have a event item specifier () */ brk = 0; error = process_item(data,dup,&i,event,NULL,&brk); if(error) { DEBUG_NUMBER("put_marker bad format returned error:",error); free(data->buffer); data->buffer=NULL; free(one_format); free(dup); return error; } if(brk) break; continue; } /* There is a format specifier ! */ start = dup+i-1; end = start; block = 0; while((*end != '(') && (*end != '\0')) end++; /* Check why we stopped */ if((*end == '\0') || (!(CHECK_VALID(*(end-1))))) { /* Someything is not right - put marker and we are done */ error = put_marker(data,BAD_FORMAT,BAD_FORMAT_LEN); if(error) { DEBUG_NUMBER("put_marker bad format returned error:",error); free(data->buffer); data->buffer=NULL; free(one_format); free(dup); return error; } /* We are done */ break; } /* We are here becuase we have a valid (hopefully) format */ block = end - start; memcpy(one_format,start,block); *(one_format+block) = '\0'; i = end - dup; brk = 0; error = process_item(data,dup,&i,event,one_format,&brk); if(error) { DEBUG_NUMBER("put_marker bad format returned error:",error); free(data->buffer); data->buffer=NULL; free(one_format); free(dup); return error; } if(brk) break; DEBUG_STRING("Format after another item:",dup+i); } free(one_format); free(dup); /* Out of the loop */ DEBUG_STRING("serialize_with_format","Success Exit"); return EOK; }