about summary refs log tree commit diff
path: root/converter/other/pngtxt.c
blob: bbbec099ea7c7f70b118f69cf16c8e613f4cdd83 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <png.h>

#include "nstring.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 
pnmpng_read_text (png_info * const info_ptr, 
                  FILE *     const tfp, 
                  bool       const ztxt,
                  bool       const verbose) {

    const char * textline;
    unsigned int lineLength;
    unsigned int commentIdx;
    bool noCommentsYet;
    bool eof;
    unsigned int allocatedComments;
        /* Number of entries currently allocated for the info_ptr->text
           array 
        */

    allocatedComments = 256;  /* initial value */

    MALLOCARRAY(info_ptr->text, allocatedComments);
    if (info_ptr->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(&info_ptr->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(&info_ptr->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(&info_ptr->text[commentIdx], 
                                    textline, lineLength);
                }
            }
            strfree(textline);
        }
    } 
    if (noCommentsYet)
        info_ptr->num_text = 0;
    else
        info_ptr->num_text = commentIdx + 1;

    if (verbose)
        pm_message("%d comments placed in text chunk", info_ptr->num_text);
}