Files
caltool/calutil.c
2019-02-14 13:56:07 -05:00

986 lines
28 KiB
C

/* calutil.c
* Date: Feb 21, 2016
* Description: Reads in an ics file and stores meaningful info in memory.
* Various status codes are provided in order to discern what
* went wrong in the file while reading it. */
#include "calutil.h"
#define BUFF_LEN 1024
/* Function name: initCalStatus
* Description: Initialize a CalStatus' members.
* Args: Code and lines to initilaize your CalStatus with.
* Returns: A CalStatus with values you passed. */
CalStatus initCalStatus(CalError code, int linefrom, int lineto) {
CalStatus status;
status.code = code;
status.linefrom = linefrom;
status.lineto = lineto;
return status;
}
/* Function name: initCalProp
* Description: Initialize a CalProp's members.
* Returns: A CalProp with nul or 0 values. */
static CalProp initCalProp(void) {
CalProp prop;
prop.name = NULL;
prop.value = NULL;
prop.nparams = 0;
prop.param = NULL;
prop.next = NULL;
return prop;
}
/* Function name: initCalComp
* Description: Initialize a CalComp's members to nul or 0
* Args: The adress of the CalComp to initialize. */
static void initCalComp(CalComp* comp) {
comp->name = NULL;
comp->prop = NULL;
comp->ncomps = 0;
comp->nprops = 0;
}
/* Function name: printBuffer
* Description: Prints a string to a file, and folds any strings that
* are longer than FOLD_LEN bytes.
* Args: The char* to print, and the stream to print it to.
* Returns: The number of lines written, or it's negative if an error. */
static int printBuffer(char* buff, FILE* ics) {
int len = strlen(buff);
int lines = 0;
int count = 0;
for(int i = 0; i < len; i++) {
if(fprintf(ics, "%c", buff[i]) < 0) {
return lines * (-1);
}
count++;
if(i == len - 1) {
if(fprintf(ics, "\r\n") < 0) {
return lines * (-1);
}
lines++;
break;
}
if(count == FOLD_LEN) {
if(fprintf(ics, "\r\n ") < 0) {
return lines * (-1);
}
lines++;
count = 1;
}
}
return lines;
}
/* Function name: printParam
* Description: Formats a string to match iCalandar syntax for parameters.
* Args: The parameter to format a string for, and the string to store it in.*/
static void printParam(CalParam* param, char** buff) {
char* comma = "";
char* temp = NULL;
if(param == NULL) {
return;
} else if(*buff == NULL) {
*buff = malloc(1);
(*buff)[0] = '\0';
assert(*buff != NULL);
}
*buff = realloc(*buff, strlen(*buff) + strlen(param->name) + 3);
assert(*buff != NULL);
temp = malloc(strlen(*buff) + strlen(param->name) + 3);
assert(temp != NULL);
sprintf(temp, "%s;%s=", *buff, param->name);
sprintf(*buff, "%s", temp);
for(int i = 0; i < param->nvalues; i++) {
*buff = realloc(*buff, strlen(*buff) + strlen(param->value[i]) + 2);
temp = realloc(temp , strlen(temp ) + strlen(param->value[i]) + 2);
assert(*buff != NULL && temp != NULL);
sprintf(temp, "%s", *buff);
sprintf(*buff, "%s%s%s", temp, comma, param->value[i]);;
comma = ",";
}
free(temp);
printParam(param->next, buff);
}
/* Function name: printProp
* Description: Recursively prints a property to a stream in iCalandar syntax.
* Args: The property to print, and the stream to print it to.
* Returns: The number of lines written, or it's negative if an error. */
static int printProp(CalProp* prop, FILE* ics) {
char* buff = NULL;
char* temp = NULL;
int lines = 0;
int ioerr = 0;
int len = 0;
if(prop == NULL) {
return 0;
} else if(prop->name == NULL) {
len = 0;
} else {
len = strlen(prop->name);
}
buff = malloc(len + 1);
assert(buff != NULL);
sprintf(buff, "%s", prop->name);
printParam(prop->param, &buff);
temp = malloc(strlen(buff) + strlen(prop->value) + 4);
assert(temp != NULL);
sprintf(temp, "%s:%s", buff, prop->value);
free(buff);
lines = printBuffer(temp, ics);
free(temp);
if(lines < 0) {
return lines;
}
ioerr = printProp(prop->next, ics);
if(ioerr < 0) {
return ((-1) * lines) + ioerr;
}
return lines + ioerr;
}
/* Function name: printComp
* Description: Recursively prints a component to a stream in iCalandar syntax.
* Args: The component to print, and the stream to print it to.
* Returns: The number of lines written, or it's negative if an error. */
static int printComp(const CalComp* comp, FILE* ics) {
int lines = 0;
int ioerr = 0;
char* buff = NULL;
if(comp == NULL) {
return 0;
}
buff = malloc(strlen(comp->name) + 9);
assert(buff != NULL);
sprintf(buff, "BEGIN:%s", comp->name);
lines = printBuffer(buff, ics);
free(buff);
if(lines < 0) {
return lines;
}
buff = NULL;
ioerr = printProp(comp->prop, ics);
if(ioerr < 0) {
return ((-1) * lines) + ioerr;
}
lines += ioerr;
for(int i = 0; i < comp->ncomps; i++) {
ioerr = printComp(comp->comp[i], ics);
if(ioerr < 0) {
return ((-1) * lines) + ioerr;
}
lines += ioerr;
}
buff = malloc(strlen(comp->name) + 7);
assert(buff != NULL);
sprintf(buff, "END:%s", comp->name);
ioerr = printBuffer(buff, ics);
free(buff);
if(ioerr < 0) {
return ((-1) * lines) + ioerr;
}
return lines + ioerr;
}
/* Function name: validateProp
* Description: Validates that there is only 1 version, and that it matches
* VCAL_VER macro. Also validates that there is only 1 prodid.
* Note: To reset static variables safely, call with (NULL, true).
* Args: The CalProp to validate
* Returns: The error code if any, OK otherwise. */
static CalError validateProp(CalComp* comp) {
int nprodid = 0;
int nversion = 0;
for(CalProp* prop = comp->prop; prop != NULL; prop = prop->next) {
if(strcmp(prop->name, "VERSION") == 0) {
nversion++;
if(strcmp(prop->value, VCAL_VER) != 0) {
return BADVER;
}
} else if (strcmp(prop->name, "PRODID") == 0) {
nprodid++;
}
}
if(nversion != 1) {
return BADVER;
} else if(nprodid != 1) {
return NOPROD;
}
return OK;
}
/* Function name: validateComp
* Description: Validates that at least one component starts with V,
* and that there is atleast one component.
* Args: The CalComp to validate.
* Returns: The error code if any, OK otherwise. */
static CalError validateComp(CalComp* comp) {
int vCount = 0; /* must have at least one comp stating with "V" */
for(int i = 0; i < comp->ncomps; i++) {
if((comp->comp[i])->name[0] == 'V') {
vCount++;
}
}
if(comp->ncomps == 0 || vCount == 0) {
return NOCAL;
}
return OK;
}
/* Function name: displace
* Description: Moves each character in a string forward by the amount of
* characters specified.
* Args: String to displace, and the number of characters to offset it by. */
static void displace(char* str, int offset) {
int len = strlen(str);
for(int i = 0; i < len - offset; i++) {
str[i] = str[i + offset];
}
str[len - offset] = '\0';
}
/* Function name: removeCRNL
* Description: Checks for \r\n sequence and removes it.
* Args: The string to search for CRNL.
* Returns: True if CRNL was found and removed, false otherwise. */
static bool removeCRNL(char* str) {
int len = strlen(str);
for(int i = 0; i < len; i++) {
switch(str[i]) {
case '\r':
str[i] = '\0';
return str[i + 1] == '\n';
case '\n':
str[i] = '\0';
return str[i - 1] == '\r';
}
}
return false;
}
/* Function name: isEmpty
* Description: Checks if a string contains only whitespace.
* Args: The string to check.
* Returns: True if string is all whitespace, false otherwise. */
static bool isEmpty(char* str) {
int len = strlen(str);
for(int i = 0; i < len; i++) {
if(isspace(str[i]) == false) {
return false;
}
}
return true;
}
/* Function name: removeLead
* Description: Checks for and removes a leading whitespace character.
* Args: The string to check for and remove leading whitespace from.
* Returns: True if string began with whitespace, false otherwise. */
static bool removeLead(char* str) {
int len = strlen(str);
int i = 0;
if(str[0] == ' ' || str[0] == '\t') {
for(i = 0; i < len - 1; i++) {
if(str[i] == '\0') {
break;
}
str[i] = str[i + 1];
}
str[i] = '\0';
return true;
}
return false;
}
/* Function name: capitalize
* Description: Converts a string to uppercase.
* Args: The string to make uppercase. */
static void capitalize(char* str) {
int len = strlen(str);
for(int i = 0; i < len; i++) {
str[i] = toupper(str[i]);
}
}
/* Function name: checkStart
* Description: Determines if a prop matches "BEGIN:VCALENDAR".
* Args: The prop to check.
* Returns: True if prop matches "BEGIN:VCALENDAR", false otherwise. */
static bool checkStart(CalProp prop) {
bool check = false;
if(prop.name == NULL || prop.value == NULL) {
return false;
}
capitalize(prop.value);
check = strcmp(prop.name, "BEGIN") == 0;
check = check && strcmp(prop.value, "VCALENDAR") == 0;
check = check && prop.nparams == 0;
return check;
}
/* Function name: parseName
* Description: Identifies the name in this string and stores it in prop->name.
* Also removes the name from the string to simplify future parse.
* Args: The string to parse a name out of, and the prop to store it in.
* Returns: True if a name was found, false otherwise. */
static bool parseName(char* str, CalProp* prop) {
int offset = 0;
int len = strlen(str);
bool success = false;
int i = 0;
if(str == NULL) {
return false;
}
for(i = 0; i < len + 1; i++) {
if(str[i] == ';' || str[i] == ':') { /* property name ends here */
if(i == 0) {
return false;
}
offset = i;
success = true;
break;
} else if(str[i] == '\0' || str[i] == '\"' || isspace(str[i])) {
free(prop->name); /* property doesn't have a value, or it has */
prop->name = NULL; /* quotes or whitespace in it */
return false;
}
prop->name = realloc(prop->name, i + 2);
assert(prop->name != NULL);
prop->name[i] = str[i];
}
prop->name[i] = '\0';
displace(str, offset);
capitalize(prop->name);
if(strlen(prop->name) == 0) {
free(prop->name);
prop->name = NULL;
success = false;
}
return success;
}
/* Function name: parseParam
* Description: Identifies the parameters in this string, and stores them in
* prop->param linked list, and increments prop->nparams for each.
* Also removes the params from string to simplify future parse.
* Args: The string to parse patameters out of, and the prop to store them in.
* Note: If a parameter contains a syntax error, str will be damaged in order
* for parseValue to throw the SYNTAX error.
* Returns: True if any parameters were found, false otherwise. */
static bool parseParam(char* str, CalProp* prop) {
int len = strlen(str);
int i = 0;
int j = 0;
int vals = 0;
int offset = 0;
CalParam* param = prop->param;
bool quoted = false;
char name [len + 1];
char value[len + 1];
char temp [len + 1];
if(str == NULL || str[0] != ';' || len == 0) {
return false;
}
for(i = 1; i < len; i++) { /* parse parameter name out */
if(str[i] == ':' || str[i] == ';') {
offset = i; /* parameter name ends here if no value */
break;
} else if(str[i] == '\"' || str[i] == ' ' || str[i] == '\t') {
str[i] = '\"';
str[0] = '\0';
return false;
} else if(quoted == false && str[i] == '=') {
vals = 1;
offset = i + 1;
break; /* parameter name ends here */
}
name[i - 1] = str[i];
}
name[i - 1] = '\0';
if(vals != 1) { /* param wasn't proper */
str[i] = '\"';
str[0] = '\0';
return false; /* return while buffer is wrecked, so future parsing */
} /* returns a SYNTAX error */
capitalize(name);
displace(str, offset);
if(strlen(name) == 0) {
return false;
}
len = strlen(str);
offset = 0;
for(i = 0; i < len; i++) { /* parse parameter value out */
if(quoted == false && (str[i] == ':' || str[i] == ';')) {
offset = i; /* parameter value ends here */
break;
} else if(quoted == false && isspace(str[i]) == true) {
str[i] = '\"';
str[0] = '\0';
return false;
}else if(str[i] == '\"') {
quoted = !quoted;
} else if(quoted == false && str[i] == ',') {
vals++; /* this parameter has multiple values */
}
value[i] = str[i];
}
value[i] = '\0';
displace(str, offset);
for(param = prop->param; param != NULL; param = param->next) {
if(param->next == NULL) { /* find the end of linked list */
break; /* parameters will be copied into the end of list */
}
}
if(prop->nparams == 0) { /* this is first parameter */
prop->param = malloc(sizeof(CalParam) + (vals * sizeof(char*)));
assert(prop->param != NULL);
param = prop->param;
} else { /* copy into the end of the linked list */
param->next = malloc(sizeof(CalParam) + (vals * sizeof(char*)));
assert(param->next != NULL);
param = param->next;
}
prop->nparams++;
param->nvalues = vals;
param->next = NULL;
param->name = malloc(strlen(name) + 1);
assert(param->name != NULL);
strncpy(param->name, name, strlen(name));
param->name[strlen(name)] = '\0';
for(i = 0; i < vals; i++) { /* seperate the values */
offset = 0;
len = strlen(value);
for(j = 0; j < len; j++) {
if(value[j] == '\"') {
quoted = !quoted;
} else if(quoted == false && value[j] == ',') {
offset = j + 1; /* a value ends here */
break;
} else if(j == len - 1) {
offset = j; /* the last value ends here */
}
temp[j] = value[j];
}
temp[j] = '\0';
displace(value, offset);
param->value[i] = malloc(strlen(temp) + 1);
assert(param->value[i] != NULL);
strncpy(param->value[i], temp, strlen(temp));
(param->value[i])[strlen(temp)] = '\0';
}
return true;
}
/* Function name: parseValue
* Description: Identifies the value in this string and stores it in prop.
* Args: The string to parse the value out of, and the prop to store it in.
* Returns: True if the value was found, false otherwise. */
static bool parseValue(char* str, CalProp* prop) {
int len = strlen(str);
if(str == NULL || str[0] != ':' || strlen(str) == 0) {
return false;
}
prop->value = malloc(len);
assert(prop->value != NULL);
for(int i = 0; i < len; i++) { /* copy the string without the colon ':' */
prop->value[i] = str[i + 1]; /* includes null terminator */
}
return true;
}
/* Function name: freeCalParam
* Description: Recursively frees allocated CalParams in this linked list.
* Args: The head of the linked list of CalParams to free. */
static void freeCalParam(CalParam* param) {
if(param == NULL) {
return;
}
free(param->name);
param->name = NULL;
freeCalParam(param->next);
free(param->next);
param->next = NULL;
for(int i = 0; i < param->nvalues; i++) {
free(param->value[i]);
param->value[i] = NULL;
}
param->nvalues = 0;
}
/* Function name: freeCalProp
* Description: Recursively frees allocated CalProp in this linked list.
* Args: The head of the linked list of CalProps to free. */
static void freeCalProp(CalProp* prop) {
if(prop == NULL) {
return;
}
free(prop->name);
prop->name = NULL;
free(prop->value);
prop->value = NULL;
freeCalParam(prop->param);
free(prop->param);
prop->param = NULL;
freeCalProp(prop->next);
free(prop->next);
prop->next = NULL;
prop->nparams = 0;
}
CalStatus readCalFile( FILE *const ics, CalComp **const pcomp ) {
CalStatus status = initCalStatus(OK, 0, 0);
char* line = NULL;
assert(ics != NULL && pcomp != NULL);
readCalLine(NULL, NULL);
*pcomp = malloc(sizeof(CalComp));
assert(*pcomp != NULL);
initCalComp(*pcomp);
status = readCalComp(ics, pcomp);
if(status.code != OK) {
freeCalComp(*pcomp);
*pcomp = NULL;
return status;
}
/* file has been read successfully. performing validations below */
status.code = validateProp(*pcomp);
if(status.code != OK) {
freeCalComp(*pcomp);
*pcomp = NULL;
return status;
}
status.code = validateComp(*pcomp);
if(status.code != OK) {
freeCalComp(*pcomp);
*pcomp = NULL;
return status;
}
readCalLine(ics, &line);
if(line != NULL) {
status.code = AFTEND;
status.linefrom++;
status.lineto++;
freeCalComp(*pcomp);
*pcomp = NULL;
}
return status;
}
CalStatus readCalComp( FILE *const ics, CalComp **const pcomp ) {
CalComp comp;
CalStatus status = initCalStatus(OK, 0, 0);
CalProp prop = initCalProp();
CalProp *temp = NULL;
char* line = NULL;
static int depth = 0; /* keeps track of nested begins */
static bool firstCall = true;
const size_t size = sizeof(CalComp); /* these constants are purely for */
const size_t sizePtr = sizeof(CalComp*); /* managing readability */
int num = 0;
int lineStart = 0;
int linesRead = 0;
if(ics == NULL && pcomp == NULL) { /* reset static var */
depth = 0;
firstCall = true;
return status;
}
initCalComp(&comp);
do {
free(line);
line = NULL;
status = readCalLine(ics, &line);
if(line == NULL || status.code != OK) {
if(firstCall == true) {
lineStart = status.linefrom + 1;
linesRead = status.lineto + 1;
status = initCalStatus(NOCAL, 0, 0);
}
break;
}
if(line == NULL && depth != 0) {
status.code = BEGEND;
break;
}
status.code = parseCalProp(line, &prop);
if(status.code != OK) {
freeCalProp(&prop);
break;
}
if((*pcomp)->name == NULL && checkStart(prop) == false) {
status.code = NOCAL; /* first component isnt BEGIN:VCALENDAR */
freeCalProp(&prop);
break;
} else if((*pcomp)->name == NULL) { /* create the first compnent */
(*pcomp)->name = malloc(strlen(prop.value) + 1);
assert((*pcomp)->name != NULL);
strncpy((*pcomp)->name, prop.value, strlen(prop.value));
(*pcomp)->name[strlen(prop.value)] = '\0';
depth = 1;
freeCalProp(&prop);
prop = initCalProp();
continue;
}
if(strcmp(prop.name, "BEGIN") == 0) {
capitalize(prop.value);
if(depth >= 3) {
status.code = SUBCOM;
freeCalProp(&prop);
break;
} else { /* create a new compnent with values from prop */
comp.name = malloc(strlen(prop.value) + 1);
assert(comp.name != NULL);
strncpy(comp.name, prop.value, strlen(prop.value));
comp.name[strlen(prop.value)] = '\0';
depth++;
freeCalProp(&prop);
prop = initCalProp();
num = (*pcomp)->ncomps;
(*pcomp)->ncomps++;
*pcomp = realloc(*pcomp, size + ((*pcomp)->ncomps) * sizePtr);
(*pcomp)->comp[num] = malloc(size);
assert((*pcomp != NULL) && ((*pcomp)->comp[num] != NULL));
*((*pcomp)->comp[num]) = comp;
/* put newly created comp into the flexible array member */
status = readCalComp(ics, &((*pcomp)->comp[num]));
/* read the next compnents into the new one */
if(status.code != OK) {
break;
}
}
} else if(strcmp(prop.name, "END") == 0) {
capitalize(prop.value);
if((*pcomp)->ncomps == 0 && (*pcomp)->nprops == 0) {
status.code = NODATA;
freeCalProp(&prop);
break;
}
if(strcmp(prop.value, (*pcomp)->name) == 0) {
if(strcmp(prop.value, "VCALENDAR") != 0) {
depth--;
} /* else we are done */
freeCalProp(&prop);
break;
} else {
status.code = BEGEND;
freeCalProp(&prop);
break;
}
} else { /* ordinary property */
for(temp = (*pcomp)->prop; temp != NULL; temp = temp->next) {
if(temp->next == NULL) {
break; /* find place in linked list to copy to */
}
}
if(temp != NULL) { /* append to linked list */
temp->next = malloc(sizeof(CalProp));
assert(temp->next != NULL);
*temp->next = prop;
temp->next->next = NULL;
} else { /* start a linked list */
(*pcomp)->prop = malloc(sizeof(CalProp));
assert((*pcomp)->prop != NULL);
*(*pcomp)->prop = prop;
(*pcomp)->prop->next = NULL;
}
(*pcomp)->nprops++;
prop = initCalProp();
}
} while(line != NULL);
if(status.code == NOCAL && depth != 0) {
status = initCalStatus(BEGEND, lineStart, linesRead);
}
firstCall = false;
free(line);
return status;
}
CalStatus readCalLine( FILE *const ics, char **const pbuff ) {
static char* readAhead = NULL; /* buffer containing content on next line */
static int linesRead = 0; /* total number of lines read thus far */
static bool lastLine = false; /* used to check if EOF was hit or not */
CalError status = OK;
char buffer[BUFF_LEN] = {'\0'};
char temp [BUFF_LEN] = {'\0'};
bool folded = true;
int startLine = linesRead + 1;
int len = 0;
int i = 0;
if(ics == NULL || pbuff == NULL) { /* NULL args | prepare for a new file */
readCalComp(NULL, NULL);
free(readAhead);
readAhead = NULL;
linesRead = 0;
lastLine = false;
return initCalStatus(OK, 0, 0);
}
assert(ics != NULL && pbuff != NULL);
if(lastLine == true) { /* EOF was hit last call */
*pbuff = NULL;
return initCalStatus(status, linesRead-1, linesRead-1);
}
if(readAhead == NULL && lastLine == false) { /* first call */
if(fgets(buffer, BUFF_LEN - 1, ics) == NULL){ /* empty file */
*pbuff = NULL;
lastLine = true;
return initCalStatus(OK, 0, 0);
} else {
linesRead++;
}
if(removeCRNL(buffer) == false) {
if(fgets(temp, BUFF_LEN - 1, ics) != NULL) {
free(*pbuff);
*pbuff = NULL;
status = NOCRNL;
folded = false;
}
} else {
*pbuff = malloc(strlen(buffer) + 1);
assert(*pbuff != NULL);
strncpy(*pbuff, buffer, strlen(buffer));
(*pbuff)[strlen(buffer)] = '\0';
}
} else { /* a string was stored last call for me */
*pbuff = malloc(strlen(readAhead) + 1);
assert(*pbuff != NULL);
strncpy(*pbuff, readAhead, strlen(readAhead));
(*pbuff)[strlen(readAhead)] = '\0';
free(readAhead);
readAhead = NULL;
linesRead++;
}
while(folded == true) {
for(i = 0; i < BUFF_LEN; i++) {
buffer[i] = '\0';
}
if(fgets(buffer, BUFF_LEN - 1, ics) == NULL) {
lastLine = true; /* let next call know not to read any further */
folded = false; /* proccess remaining string, quit afterward */
free(readAhead);
readAhead = NULL;
} else if(removeCRNL(buffer) == false) {
if(fgets(temp, BUFF_LEN - 1, ics) != NULL) {
free(*pbuff);
*pbuff = NULL;
status = NOCRNL;
break;
}
}
if(isEmpty(buffer) == true) {
linesRead++;
}
folded = removeLead(buffer) && status == OK;
if(strlen(buffer) == 0) {
if(*pbuff == NULL) {
*pbuff = malloc(1);
assert(*pbuff != NULL);
(*pbuff)[0] = '\0';
}
continue;
} else if(folded == true) { /* append buffer to the end of pbuff */
if(*pbuff == NULL) {
len = strlen(buffer) + 1;
*pbuff = malloc(len);
} else {
len = strlen(*pbuff) + strlen(buffer) + 1;
*pbuff = realloc(*pbuff, len);
}
assert(*pbuff != NULL);
strncat(*pbuff, buffer, strlen(buffer));
(*pbuff)[len - 1] = '\0';
linesRead++;
} else { /* store buffer away for use on next call */
readAhead = malloc(strlen(buffer) + 1);
assert(readAhead != NULL);
strncpy(readAhead, buffer, strlen(buffer));
readAhead[strlen(buffer)] = '\0';
}
}
if(lastLine == true) {
linesRead--;
}
return initCalStatus(status, startLine, linesRead);
}
CalError parseCalProp( char *const buff, CalProp *const prop ) {
int len = strlen(buff);
char buffer[len + 1];
*prop = initCalProp();
assert(buff != NULL && prop != NULL);
strncpy(buffer, buff, len);
buffer[len] = '\0';
if(parseName(buffer, prop) == false) {
freeCalProp(prop);
*prop = initCalProp();
return SYNTAX;
}
while(parseParam(buffer, prop) == true) {
/* optional parameters */
}
if(parseValue(buffer, prop) == false) {
freeCalProp(prop);
*prop = initCalProp();
return SYNTAX;
}
return OK;
}
void freeCalComp( CalComp *const comp ) {
if(comp == NULL) {
return;
}
free(comp->name);
comp->name = NULL;
freeCalProp(comp->prop);
free(comp->prop);
comp->prop = NULL;
for(int i = 0; i < comp->ncomps; i++) {
freeCalComp(comp->comp[i]);
comp->comp[i] = NULL;
}
free(comp);
}
CalStatus writeCalComp( FILE *const ics, const CalComp *comp ) {
int lines = 0;
if(ics == NULL || comp == NULL) {
return initCalStatus(IOERR, 0, 0);
}
lines = printComp(comp, ics);
if(lines < 1) {
return initCalStatus(IOERR, (-1) * lines, (-1) * lines);
}
return initCalStatus(OK, lines, lines);
}