#include #include #include #include #include "nstring.h" #include "pngx.h" #include "pngtxt.h" #include "pm.h" #include "mallocvar.h" #define MAXCOMMENTS 256 static void readOffKey(char const textline[], unsigned int const lineLength, unsigned int * const cursorP, char ** const keyP) { /* Get the comment key */ char * cp; unsigned int cursor; cursor = *cursorP; MALLOCARRAY(cp, lineLength + 1); /* leave room for terminating NUL */ if (cp == NULL) pm_error("Unable to allocate memory for text chunks"); *keyP = cp; if (textline[0] == '"') { ++cursor; /* skip past opening " */ while (textline[cursor] != '"') { if (textline[cursor] == '\0') { *cp = '\0'; pm_error("Invalid comment file format: keyword contains " "a NUL character. Text leading up to the NUL " "character is '%s'", *keyP); } *(cp++) = textline[cursor++]; } ++cursor; /* skip past closing " */ } else { while (cursor < lineLength && textline[cursor] != ' ' && textline[cursor] != '\t' && textline[cursor] != '\0') *(cp++) = textline[cursor++]; } *cp++ = '\0'; *cursorP = cursor; } static void startComment(struct png_text_struct * const commentP, char const textline[], unsigned int const lineLength, bool const compressed) { /*---------------------------------------------------------------------------- Assuming 'textline' is the first line of a comment in a comment file, put the information from it in the comment record *commentP. Use the text on this line as the comment text, even though the true comment text may include text from subsequent continuation lines as well. 'textline' is not NUL-terminated. Its length is 'lineLength', and it is at least one character long. 'textline' does not contain a newline character. 'compressed' means the comment text is compressed. -----------------------------------------------------------------------------*/ unsigned int cursor; /* the following is a not that accurate check on Author or Title */ if ((!compressed) || (textline[0] == 'A') || (textline[0] == 'T')) commentP->compression = -1; else commentP->compression = 0; cursor = 0; readOffKey(textline, lineLength, &cursor, &commentP->key); /* skip over delimiters between key and comment text */ while (cursor < lineLength && (textline[cursor] == ' ' || textline[cursor] == '\t' || textline[cursor] == '\0')) ++cursor; { /* Get the first line of the comment text */ unsigned int const startPos = cursor; char *cp; MALLOCARRAY(cp, lineLength+1); /* leave room for safety NUL */ if (!cp) pm_error("Unable to allocate memory for text chunks"); memcpy(cp, textline + startPos, lineLength - startPos); cp[lineLength - startPos] = '\0'; /* for safety - not part of text */ commentP->text = cp; commentP->text_length = lineLength - startPos; } } static void continueComment(struct png_text_struct * const commentP, char const textline[], unsigned int const lineLength) { /*---------------------------------------------------------------------------- Update the comment record *commentP by adding to it the text from textline[], which is a continuation line from a comment file. 'textline' is not NUL-terminated. Its length is 'lineLength', and it is at least one character long. 'textline' does not contain a newline character. -----------------------------------------------------------------------------*/ unsigned int cursor; /* cursor into textline[] */ unsigned int const newTextLength = commentP->text_length + lineLength + 1 + 1; REALLOCARRAY(commentP->text, newTextLength); if (commentP->text == NULL) pm_error("Unable to allocate %u bytes of memory for comment chunk", newTextLength); commentP->text[commentP->text_length++] = '\n'; /* Skip past leading delimiter characters in file line */ cursor = 0; while (textline[cursor] == ' ' || textline[cursor] == '\t' || textline[cursor] == '\0') ++cursor; memcpy(commentP->text + commentP->text_length, textline + cursor, lineLength - cursor); commentP->text_length += lineLength - cursor; commentP->text[commentP->text_length] = '\0'; /* for safety */ } static void getFileLine(FILE * const fileP, const char ** const textP, unsigned int * const lengthP) { /*---------------------------------------------------------------------------- Read the next line (characters from current position through the first newline character) and return it. Put the text in newly malloc'ed storage. Do not include the newline. Add a terminating NUL for safety, but note that you can't rely on this as the end of line marker because the line may contain a NUL. *lengthP does not include the NUL that we add. If there are no more characters in the file, return NULL. -----------------------------------------------------------------------------*/ char * textline; /* malloc'ed */ unsigned int cursor; /* cursor into textline[] */ unsigned int allocated; /* The number of characters of space that are allocated for 'textline' */ bool eol; bool gotSomething; allocated = 128; /* initial value */ MALLOCARRAY(textline, allocated); if (textline == NULL) pm_error("Unable to allocate buffer to read a line of a file."); cursor = 0; eol = FALSE; gotSomething = FALSE; while (!eol) { int const c = getc(fileP); if (c != EOF) gotSomething = TRUE; if (c == '\n' || c == EOF) eol = TRUE; else { if (cursor > allocated - 1 - 1) { /* leave space for safety NUL */ allocated *= 2; REALLOCARRAY(textline, allocated); if (textline == NULL) pm_error("Unable to allocate buffer to read a line of " "a file."); } textline[cursor++] = c; } } textline[cursor] = '\0'; /* For safety; not part of line */ if (gotSomething) { *textP = textline; *lengthP = cursor; } else { free(textline); *textP = NULL; } } static void handleArrayAllocation(png_text ** const arrayP, unsigned int * const allocatedCommentsP, unsigned int const commentIdx) { if (commentIdx >= *allocatedCommentsP) { *allocatedCommentsP *= 2; REALLOCARRAY(*arrayP, *allocatedCommentsP); if (*arrayP == NULL) pm_error("unable to allocate memory for comment array"); } } /****************************************************************************** EXTERNAL SUBROUTINES ******************************************************************************/ void pngtxt_read(struct pngx * const pngxP, FILE * const tfp, bool const ztxt, bool const verbose) { const char * textline; unsigned int lineLength; unsigned int commentIdx; bool noCommentsYet; bool eof; png_textp text; /* An array; one line per element */ unsigned int allocatedComments; /* Number of entries currently allocated for the PNG text array */ allocatedComments = 256; /* initial value */ MALLOCARRAY(text, allocatedComments); if (text == NULL) pm_error("unable to allocate memory for comment array"); commentIdx = 0; noCommentsYet = TRUE; eof = FALSE; while (!eof) { getFileLine(tfp, &textline, &lineLength); if (textline == NULL) eof = TRUE; else { if (lineLength == 0) { /* skip this empty line */ } else { handleArrayAllocation(&text, &allocatedComments, commentIdx); if ((textline[0] != ' ') && (textline[0] != '\t')) { /* Line doesn't start with white space, which means it starts a new comment. */ if (noCommentsYet) { /* No previous comment to move past */ } else ++commentIdx; noCommentsYet = FALSE; startComment(&text[commentIdx], textline, lineLength, ztxt); } else { /* Line starts with whitespace, which means it is a continuation of the current comment. */ if (noCommentsYet) pm_error("Invalid comment file format: " "first line is a continuation line! " "(It starts with whitespace)"); continueComment(&text[commentIdx], textline, lineLength); } } pm_strfree(textline); } } if (!noCommentsYet) pngx_setText(pngxP, text, commentIdx + 1); if (verbose) pm_message("%d comments placed in text chunk", commentIdx + 1); free(text); }