[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[pdf-devel] [PATCH] Token reader, with new API
From: |
Michael Gold |
Subject: |
[pdf-devel] [PATCH] Token reader, with new API |
Date: |
Wed, 20 May 2009 01:35:11 -0400 |
User-agent: |
Mutt/1.5.18 (2008-05-17) |
Here's a patch to add a token reader based on the documented API.
I've also added and documented a pdf_token_equal_p function.
The code hasn't changed much since the last tokeniser patch I sent,
apart from being restructured for the new API.
-- Michael
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: address@hidden
# target_branch: file:///home/michael/src/%2Blocal/gnupdf/trunk/
# testament_sha1: 2b52a6d550745f1e76c44bb24c5ce08ea05be6e8
# timestamp: 2009-05-20 01:28:41 -0400
# base_revision_id: address@hidden
#
# Begin patch
=== modified file 'AUTHORS'
--- AUTHORS 2009-05-19 18:03:37 +0000
+++ AUTHORS 2009-05-20 04:35:27 +0000
@@ -261,8 +261,12 @@
torture/test-obj_dupequality.c torture/test-rectangle.c
torture/test-stm_openclose.c
-Michael Gold: changed src/Makefile.am utils/Makefile.am
- torture/unit/Makefile.am src/gnupdf.texi
+Michael Gold: wrote src/base/pdf-token-obj.c src/base/pdf-token-obj.h
+ src/base/pdf-token-reader.c src/base/pdf-token-reader.h
+ torture/unit/base/token/pdf-token-read.c
+ torture/unit/base/token/tsuite-token.c utils/toktest.c
+and changed src/Makefile.am utils/Makefile.am torture/unit/Makefile.am
+ doc/gnupdf.texi src/base/pdf-error.h torture/unit/runtests.c
Yang Chang Hua: wrote src/base/pdf-stm-f-dct.h
src/base/pdf-stm-f-dct.c
=== modified file 'ChangeLog'
--- ChangeLog 2009-05-19 20:17:49 +0000
+++ ChangeLog 2009-05-20 04:35:27 +0000
@@ -1,3 +1,33 @@
+2009-05-20 Michael Gold <address@hidden>
+
+ Implement the token reader API.
+
+ * src/base/pdf-token-obj.c: New file, based on src/object/pdf-obj.c
+
+ * src/base/pdf-token-obj.h: Likewise
+
+ * src/base/pdf-token-reader.c: New file
+
+ * src/base/pdf-token-reader.h: New file
+
+ * src/base/pdf-error.h: Add PDF_EBADFILE, PDF_EIMPLLIMIT error codes
+
+ * src/Makefile.am: Add token reader support
+
+ * doc/gnupdf.text: Document pdf_token_equal_p
+
+ * torture/unit/base/token/pdf-token-read.c: New file
+
+ * torture/unit/base/token/tsuite-token.c: New file
+
+ * torture/unit/Makefile.am: Add token reader testcases
+
+ * torture/unit/runtests.c: Likewise
+
+ * utils/toktest.c: New file
+
+ * utils/Makefile.am: Build toktest
+
2009-05-19 David Vazquez <address@hidden>
* src/base/pdf-crypt.h: Use STATIC_INLINE indeed of EXTERN_INLINE.
=== modified file 'doc/gnupdf.texi'
--- doc/gnupdf.texi 2009-05-19 18:03:37 +0000
+++ doc/gnupdf.texi 2009-05-20 04:35:27 +0000
@@ -10498,8 +10498,29 @@
@end table
@end deftypefun
address@hidden TODO: pdf_bool_t pdf_token_equal_p (pdf_token_t token1,
pdf_token_t token2);
address@hidden -- if needed
address@hidden pdf_bool_t pdf_token_equal_p (const pdf_token_t @var{token1},
const pdf_token_t @var{token2})
+
+Determines whether the given tokens are equivalent.
+
address@hidden @strong
address@hidden Parameters
address@hidden @var
address@hidden token1
+A token.
address@hidden token2
+Another token.
address@hidden table
address@hidden Returns
+A boolean value:
address@hidden @code
address@hidden PDF_TRUE
+The given tokens are equal.
address@hidden PDF_FALSE
+The tokens are not equal.
address@hidden table
address@hidden table
+
address@hidden deftypefun
@deftypefun pdf_i32_t pdf_token_get_integer_value (const pdf_token_t
@var{token})
=== modified file 'src/Makefile.am'
--- src/Makefile.am 2009-05-05 13:58:10 +0000
+++ src/Makefile.am 2009-05-20 04:50:13 +0000
@@ -82,6 +82,9 @@
FP_MODULE_SOURCES = base/pdf-fp.h base/pdf-fp.c \
base/pdf-fp-func.h base/pdf-fp-func.c
+TOKEN_MODULE_SOURCES = base/pdf-token-obj.c base/pdf-token-obj.h \
+ base/pdf-token-reader.c base/pdf-token-reader.h
+
BASE_LAYER_SOURCES = base/pdf-base.c base/pdf-base.h \
$(ALLOC_MODULE_SOURCES) \
$(TYPES_MODULE_SOURCES) \
@@ -93,7 +96,8 @@
$(TEXT_MODULE_SOURCES) \
$(HASH_MODULE_SOURCES) \
$(TIME_MODULE_SOURCES) \
- $(CRYPT_MODULE_SOURCES)
+ $(CRYPT_MODULE_SOURCES) \
+ $(TOKEN_MODULE_SOURCES)
# Library sources
@@ -136,7 +140,9 @@
base/pdf-stm.h \
base/pdf-hash-helper.h \
base/pdf-crypt.h \
- base/pdf-fp-func.h
+ base/pdf-fp-func.h \
+ base/pdf-token-obj.h \
+ base/pdf-token-reader.h
pdf.h : $(PUBLIC_HDRS)
chmod +x $(top_builddir)/src/extract-public-hdr
=== modified file 'src/base/pdf-error.h'
--- src/base/pdf-error.h 2009-05-11 14:47:49 +0000
+++ src/base/pdf-error.h 2009-05-19 18:19:15 +0000
@@ -144,7 +144,9 @@
ERROR_ENTRY (PDF_ETYPE0, "/FunctionType 0: Error"), \
ERROR_ENTRY (PDF_ETYPE2, "/FunctionType 2: Error"), \
ERROR_ENTRY (PDF_ETYPE3, "/FunctionType 3: Error"), \
- ERROR_ENTRY (PDF_ETYPE4, "/FunctionType 4: Error")
+ ERROR_ENTRY (PDF_ETYPE4, "/FunctionType 4: Error"), \
+ ERROR_ENTRY (PDF_EBADFILE, "file violates PDF specifications"), \
+ ERROR_ENTRY (PDF_EIMPLLIMIT, "implementation limit exceeded")
#define ERROR_ENTRY(id,string) id
enum pdf_status_e
=== added file 'src/base/pdf-token-obj.c'
--- src/base/pdf-token-obj.c 1970-01-01 00:00:00 +0000
+++ src/base/pdf-token-obj.c 2009-05-20 05:19:37 +0000
@@ -0,0 +1,405 @@
+/* -*- mode: C -*- Time-stamp: "2009-05-20 04:50:13 mgold"
+ *
+ * File: pdf-token-obj.c
+ * Date: Sat Jul 7 03:04:30 2007
+ *
+ * GNU PDF Library - PDF token objects
+ *
+ */
+
+/* Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. */
+
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+
+#include "config.h"
+#include "pdf-token-obj.h"
+#include "pdf-alloc.h"
+
+/* Private functions */
+
+static INLINE pdf_status_t
+pdf_token_new (enum pdf_token_type_e type, pdf_token_t *token)
+{
+ pdf_token_t new;
+ assert (token);
+
+ new = (pdf_token_t) pdf_alloc (sizeof (struct pdf_token_s));
+ if (!new)
+ return PDF_ENOMEM;
+
+ new->type = type;
+ *token = new;
+ return PDF_OK;
+}
+
+pdf_status_t
+pdf_token_destroy (pdf_token_t token)
+{
+ assert (token);
+ switch (token->type)
+ {
+ case PDF_TOKEN_STRING: /* fall through */
+ case PDF_TOKEN_NAME: /* fall through */
+ case PDF_TOKEN_KEYWORD: /* fall through */
+ case PDF_TOKEN_COMMENT:
+ {
+ pdf_dealloc (token->value.buffer.data);
+ break;
+ }
+ default:
+ {
+ /* NOP */
+ break;
+ }
+ }
+
+ pdf_dealloc (token);
+ return PDF_OK;
+}
+
+static pdf_status_t
+pdf_token_buffer_new (enum pdf_token_type_e type,
+ const pdf_char_t *value,
+ pdf_size_t size,
+ pdf_bool_t nullterm,
+ pdf_token_t *token)
+{
+ pdf_token_t new_obj = NULL;
+ pdf_status_t rv = pdf_token_new (type, &new_obj);
+ if (rv != PDF_OK)
+ goto fail;
+
+ rv = PDF_ENOMEM;
+ new_obj->value.buffer.data = pdf_alloc (nullterm ? size + 1 : size);
+ if (!new_obj->value.buffer.data)
+ goto fail;
+
+ new_obj->value.buffer.size = size;
+ memcpy (new_obj->value.buffer.data, value, size);
+ if (nullterm)
+ new_obj->value.buffer.data[size] = 0;
+
+ *token = new_obj;
+ return PDF_OK;
+
+fail:
+ if (new_obj)
+ pdf_dealloc (new_obj);
+ return rv;
+}
+
+
+/* General functions */
+
+enum pdf_token_type_e
+pdf_token_get_type (const pdf_token_t token)
+{
+ assert (token);
+ return token->type;
+}
+
+pdf_bool_t
+pdf_token_equal_p (const pdf_token_t token1, const pdf_token_t token2)
+{
+ assert (token1 && token2);
+ if (token1->type != token2->type)
+ return PDF_FALSE;
+
+ switch (token1->type)
+ {
+ case PDF_TOKEN_DICT_START: /* fall through */
+ case PDF_TOKEN_DICT_END: /* fall through */
+ case PDF_TOKEN_ARRAY_START: /* fall through */
+ case PDF_TOKEN_ARRAY_END: /* fall through */
+ case PDF_TOKEN_PROC_START: /* fall through */
+ case PDF_TOKEN_PROC_END:
+ return PDF_TRUE;
+
+ case PDF_TOKEN_INTEGER:
+ return token1->value.integer == token2->value.integer;
+
+ case PDF_TOKEN_REAL:
+ return token1->value.real == token2->value.real;
+
+ case PDF_TOKEN_COMMENT:
+ if (token1->value.comment.continued != token2->value.comment.continued)
+ return PDF_FALSE;
+
+ /* fall through */
+ case PDF_TOKEN_STRING: /* fall through */
+ case PDF_TOKEN_NAME: /* fall through */
+ case PDF_TOKEN_KEYWORD:
+ {
+ struct pdf_token_buffer_s *buf1 = &token1->value.buffer;
+ struct pdf_token_buffer_s *buf2 = &token2->value.buffer;
+ return (buf1->size == buf2->size
+ && ( buf1->data == buf2->data
+ || !memcmp (buf1->data, buf2->data, buf1->size) ));
+ }
+
+ default:
+ assert (0); /* shouldn't happen */
+ return 0;
+ }
+}
+
+pdf_status_t
+pdf_token_dup (const pdf_token_t token, pdf_token_t *new)
+{
+ assert (token);
+ switch (token->type)
+ {
+ case PDF_TOKEN_DICT_START: /* fall through */
+ case PDF_TOKEN_DICT_END: /* fall through */
+ case PDF_TOKEN_ARRAY_START: /* fall through */
+ case PDF_TOKEN_ARRAY_END: /* fall through */
+ case PDF_TOKEN_PROC_START: /* fall through */
+ case PDF_TOKEN_PROC_END:
+ return pdf_token_valueless_new (token->type, new);
+
+ case PDF_TOKEN_INTEGER:
+ return pdf_token_integer_new (token->value.integer, new);
+
+ case PDF_TOKEN_REAL:
+ return pdf_token_real_new (token->value.real, new);
+
+ case PDF_TOKEN_STRING:
+ return pdf_token_string_new (token->value.buffer.data,
+ token->value.buffer.size,
+ new);
+ case PDF_TOKEN_NAME:
+ return pdf_token_name_new (token->value.buffer.data,
+ token->value.buffer.size,
+ new);
+ case PDF_TOKEN_KEYWORD:
+ return pdf_token_keyword_new (token->value.buffer.data,
+ token->value.buffer.size,
+ new);
+ case PDF_TOKEN_COMMENT:
+ return pdf_token_comment_new (token->value.comment.data,
+ token->value.comment.size,
+ token->value.comment.continued,
+ new);
+ default:
+ /* Should not be reached: make the compiler happy */
+ return PDF_EBADDATA;
+ }
+}
+
+pdf_status_t
+pdf_token_valueless_new (enum pdf_token_type_e type,
+ pdf_token_t *token)
+{
+ switch (type)
+ {
+ case PDF_TOKEN_DICT_START: /* fall through */
+ case PDF_TOKEN_DICT_END: /* fall through */
+ case PDF_TOKEN_ARRAY_START: /* fall through */
+ case PDF_TOKEN_ARRAY_END: /* fall through */
+ case PDF_TOKEN_PROC_START: /* fall through */
+ case PDF_TOKEN_PROC_END:
+ return pdf_token_new (type, token);
+ default:
+ return PDF_EBADDATA;
+ }
+}
+
+
+/** integers *****/
+
+pdf_status_t
+pdf_token_integer_new (pdf_i32_t value, pdf_token_t *token)
+{
+ pdf_status_t rv = pdf_token_new (PDF_TOKEN_INTEGER, token);
+ if (rv == PDF_OK)
+ (*token)->value.integer = value;
+
+ return rv;
+}
+
+pdf_i32_t
+pdf_token_get_integer_value (const pdf_token_t token)
+{
+ assert (token && token->type == PDF_TOKEN_INTEGER);
+ return token->value.integer;
+}
+
+
+/** reals *****/
+
+pdf_status_t
+pdf_token_real_new (pdf_real_t value, pdf_token_t *token)
+{
+ pdf_status_t rv;
+
+ if (isnan(value) || isinf(value))
+ return PDF_EBADDATA;
+
+ rv = pdf_token_new (PDF_TOKEN_REAL, token);
+ if (rv == PDF_OK)
+ (*token)->value.real = value;
+
+ return rv;
+}
+
+pdf_real_t
+pdf_token_get_real_value (const pdf_token_t token)
+{
+ assert (token && token->type == PDF_TOKEN_REAL);
+ return token->value.real;
+}
+
+
+/** names *****/
+
+pdf_status_t
+pdf_token_name_new (const pdf_char_t *value,
+ pdf_size_t size,
+ pdf_token_t *token)
+{
+ pdf_size_t i;
+ for (i = 0; i < size; ++i)
+ {
+ if (value[i] == 0) /* names can't include null bytes */
+ return PDF_EBADDATA;
+ }
+
+ return pdf_token_buffer_new (PDF_TOKEN_NAME, value, size, 1, token);
+}
+
+pdf_size_t
+pdf_token_get_name_size (const pdf_token_t name)
+{
+ assert (name && name->type == PDF_TOKEN_NAME);
+ return name->value.buffer.size;
+}
+
+const pdf_char_t *
+pdf_token_get_name_data (const pdf_token_t name)
+{
+ assert (name && name->type == PDF_TOKEN_NAME);
+ return name->value.buffer.data;
+}
+
+
+/** strings *****/
+
+pdf_status_t
+pdf_token_string_new (const pdf_char_t *value,
+ pdf_size_t size,
+ pdf_token_t *token)
+{
+ return pdf_token_buffer_new (PDF_TOKEN_STRING, value, size, 0, token);
+}
+
+pdf_size_t
+pdf_token_get_string_size (const pdf_token_t token)
+{
+ assert (token && token->type == PDF_TOKEN_STRING);
+ return token->value.buffer.size;
+}
+
+const pdf_char_t *
+pdf_token_get_string_data (const pdf_token_t token)
+{
+ assert (token && token->type == PDF_TOKEN_STRING);
+ return token->value.buffer.data;
+}
+
+
+/** comments *****/
+
+pdf_status_t
+pdf_token_comment_new (const pdf_char_t *value,
+ pdf_size_t size,
+ pdf_bool_t continued,
+ pdf_token_t *token)
+{
+ pdf_status_t rv;
+ pdf_size_t i;
+ for (i = 0; i < size; ++i)
+ {
+ /* comments can't span multiple lines */
+ if (pdf_is_eol_char(value[i]))
+ return PDF_EBADDATA;
+ }
+
+ rv = pdf_token_buffer_new (PDF_TOKEN_COMMENT, value, size, 0, token);
+ if (rv == PDF_OK)
+ (*token)->value.comment.continued = !!continued;
+
+ return rv;
+}
+
+pdf_size_t
+pdf_token_get_comment_size (const pdf_token_t comment)
+{
+ assert (comment && comment->type == PDF_TOKEN_COMMENT);
+ return comment->value.buffer.size;
+}
+
+const pdf_char_t *
+pdf_token_get_comment_data (const pdf_token_t comment)
+{
+ assert (comment && comment->type == PDF_TOKEN_COMMENT);
+ return comment->value.buffer.data;
+}
+
+pdf_bool_t
+pdf_token_get_comment_continued (const pdf_token_t comment)
+{
+ assert (comment && comment->type == PDF_TOKEN_COMMENT);
+ return comment->value.comment.continued;
+}
+
+
+/** keywords *****/
+
+pdf_status_t
+pdf_token_keyword_new (const pdf_char_t *value,
+ pdf_size_t size,
+ pdf_token_t *token)
+{
+ pdf_size_t i;
+ for (i = 0; i < size; ++i)
+ {
+ /* keywords can only include regular characters */
+ if (!pdf_is_regular_char(value[i]))
+ return PDF_EBADDATA;
+ }
+
+ return pdf_token_buffer_new (PDF_TOKEN_KEYWORD, value, size, 1, token);
+}
+
+pdf_size_t
+pdf_token_get_keyword_size (const pdf_token_t keyword)
+{
+ assert (keyword && keyword->type == PDF_TOKEN_KEYWORD);
+ return keyword->value.buffer.size;
+}
+
+const pdf_char_t *
+pdf_token_get_keyword_data (const pdf_token_t keyword)
+{
+ assert (keyword && keyword->type == PDF_TOKEN_KEYWORD);
+ return keyword->value.buffer.data;
+}
+
+
+/* End of pdf-token-obj.c */
=== added file 'src/base/pdf-token-obj.h'
--- src/base/pdf-token-obj.h 1970-01-01 00:00:00 +0000
+++ src/base/pdf-token-obj.h 2009-05-20 05:28:26 +0000
@@ -0,0 +1,210 @@
+/* -*- mode: C -*- Time-stamp: "2009-05-20 04:01:14 mgold"
+ *
+ * File: pdf-token-obj.h
+ * Date: Sat Jul 7 01:10:11 2007
+ *
+ * GNU PDF Library - PDF token objects
+ *
+ */
+
+/* Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. */
+
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* pdf-token-obj.{c,h} implement the token ADT, used to represent
+ all types of tokens used by the token reader and writer:
+ - Integer number
+ - Real number
+ - String
+ - Name
+ - Keyword
+ - Comment
+ - Valueless tokens:
+ - PDF_TOKEN_DICT_START
+ - PDF_TOKEN_DICT_END
+ - PDF_TOKEN_ARRAY_START
+ - PDF_TOKEN_ARRAY_END
+ - PDF_TOKEN_PROC_START
+ - PDF_TOKEN_PROC_END
+*/
+
+#ifndef PDF_TOKEN_OBJ_H
+#define PDF_TOKEN_OBJ_H
+
+#include "config.h"
+#include "pdf-types.h"
+#include "pdf-base.h"
+
+/* BEGIN PUBLIC */
+/* pdf-token-obj.h */
+
+enum pdf_token_type_e
+{
+ PDF_TOKEN_INTEGER = 1,
+ PDF_TOKEN_REAL = 2,
+ PDF_TOKEN_STRING = 3,
+ PDF_TOKEN_NAME = 4,
+ PDF_TOKEN_KEYWORD = 5,
+ PDF_TOKEN_COMMENT = 6,
+ PDF_TOKEN_DICT_START = 7,
+ PDF_TOKEN_DICT_END = 8,
+ PDF_TOKEN_ARRAY_START = 9,
+ PDF_TOKEN_ARRAY_END = 10,
+ PDF_TOKEN_PROC_START = 11,
+ PDF_TOKEN_PROC_END = 12,
+};
+
+struct pdf_token_s; /* opaque type */
+typedef struct pdf_token_s *pdf_token_t;
+
+/* Token creation */
+pdf_status_t pdf_token_integer_new (pdf_i32_t value, pdf_token_t *token);
+pdf_status_t pdf_token_real_new (pdf_real_t value, pdf_token_t *token);
+pdf_status_t pdf_token_string_new (const pdf_char_t *value, pdf_size_t size,
+ pdf_token_t *token);
+pdf_status_t pdf_token_name_new (const pdf_char_t *value, pdf_size_t size,
+ pdf_token_t *token);
+pdf_status_t pdf_token_keyword_new (const pdf_char_t *value, pdf_size_t size,
+ pdf_token_t *token);
+pdf_status_t pdf_token_comment_new (const pdf_char_t *value, pdf_size_t size,
+ pdf_bool_t continued, pdf_token_t *token);
+pdf_status_t pdf_token_valueless_new (enum pdf_token_type_e type,
+ pdf_token_t *token);
+pdf_status_t pdf_token_dup (const pdf_token_t token, pdf_token_t *new);
+
+/* Token destruction */
+pdf_status_t pdf_token_destroy (pdf_token_t token);
+
+/* Common functions */
+enum pdf_token_type_e pdf_token_type (const pdf_token_t token);
+pdf_bool_t pdf_token_equal_p (const pdf_token_t token1,
+ const pdf_token_t token2);
+
+/* Managing tokens of numeric types */
+pdf_i32_t pdf_token_get_integer_value (const pdf_token_t token);
+pdf_real_t pdf_token_get_real_value (const pdf_token_t token);
+
+/* Managing strings */
+pdf_size_t pdf_token_get_string_size (pdf_token_t token);
+const pdf_char_t *pdf_token_get_string_data (pdf_token_t token);
+
+/* Managing names */
+pdf_size_t pdf_token_get_name_size (const pdf_token_t token);
+const pdf_char_t *pdf_token_get_name_data (const pdf_token_t token);
+
+/* Managing keywords */
+pdf_size_t pdf_token_get_keyword_size (const pdf_token_t token);
+const pdf_char_t *pdf_token_get_keyword_data (const pdf_token_t token);
+
+/* Managing comments */
+pdf_size_t pdf_token_get_comment_size (const pdf_token_t token);
+const pdf_char_t *pdf_token_get_comment_data (const pdf_token_t token);
+pdf_bool_t pdf_token_get_comment_continued (const pdf_token_t token);
+
+/* END PUBLIC */
+
+
+static INLINE int
+pdf_is_wspace_char (pdf_char_t ch)
+{
+ /* ASCII codes for NUL, HT, LF, FF, CR, SP */
+ return (ch == 0 || ch == 9 || ch == 10 || ch == 12 || ch == 13 || ch == 32);
+}
+
+static INLINE int
+pdf_is_delim_char (pdf_char_t ch)
+{
+ /* ASCII codes for '%', '(', ')', '/'; '<', '>', '[', ']'; '{', '}' */
+ return (ch == 37 || ch == 40 || ch == 41 || ch == 47
+ || ch == 60 || ch == 62 || ch == 91 || ch == 93
+ || ch == 123 || ch == 125);
+}
+
+static INLINE int
+pdf_is_eol_char (pdf_char_t ch)
+{
+ return ch == 10 || ch == 13;
+}
+
+static INLINE int
+pdf_is_regular_char (pdf_char_t ch)
+{
+ return !pdf_is_wspace_char (ch) && !pdf_is_delim_char (ch);
+}
+
+/* According to the PDF reference, a PDF name object is an atomic
+ symbol uniquely defined by a sequence of non-null characters. It has
+ no internal structure.
+
+ In the practice, PDF uses name objects in order to store
+ information (such as font names). In that situation, it is
+ recommended to code such information in UTF-8. Due to this
+ stupidity we should store the entire byte sequence that conform the
+ name.
+
+ A pdf_token_buffer_s structure is used to store a name's value. */
+
+
+/* A PDF string is a sequence of bytes, in the range of 0-255. In
+ particular it may contain NULL characters (code 0 in the ASCII
+ CCS).
+
+ Corollary: NEVER NEVER NEVER EVER use a PDF string as an input
+ expecting null-terminated strings. You have been warned.
+
+ A pdf_token_buffer_s structure is used to store a string's value. */
+
+
+/* pdf_token_buffer_s is an internal structure used for tokens with an
+ * associated byte array. 'data' may or may not be null-terminated,
+ * but size never includes the trailing null. */
+struct pdf_token_buffer_s
+{
+ pdf_char_t *data;
+ pdf_size_t size;
+};
+
+struct pdf_comment_s
+{
+ pdf_char_t *data;
+ pdf_size_t size;
+ /* This structure shares a common initial sequence with pdf_token_buffer_s,
+ * so the above fields can be accessed via token->value.buffer. */
+
+ pdf_bool_t continued; /* is data continued from a previous token? */
+};
+
+
+/* A `pdf_token_s' structure stores a PDF object. The object may be of
+ any type (including NULL). */
+
+struct pdf_token_s
+{
+ enum pdf_token_type_e type;
+
+ union
+ {
+
+ struct pdf_token_buffer_s buffer;
+ pdf_i32_t integer;
+ pdf_real_t real;
+ struct pdf_comment_s comment;
+
+ } value;
+};
+
+#endif /* PDF_TOKEN_OBJ_H */
+
+/* End of pdf-token-obj.h */
=== added file 'src/base/pdf-token-reader.c'
--- src/base/pdf-token-reader.c 1970-01-01 00:00:00 +0000
+++ src/base/pdf-token-reader.c 2009-05-20 05:19:37 +0000
@@ -0,0 +1,915 @@
+/* -*- mode: C -*- Time-stamp: "2009-05-20 04:01:14 mgold"
+ *
+ * File: pdf-token-reader.c
+ * Date: Mon Dec 29 00:45:09 2008
+ *
+ * GNU PDF Library - Stream tokeniser
+ *
+ */
+
+/* Copyright (C) 2008, 2009 Free Software Foundation, Inc. */
+
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include "pdf-token-reader.h"
+
+static INLINE int can_store_char (pdf_token_reader_t reader);
+static INLINE pdf_status_t store_char (pdf_token_reader_t reader,
+ pdf_char_t ch);
+static pdf_status_t exit_state (pdf_token_reader_t reader, pdf_u32_t flags,
+ pdf_token_t *token);
+static pdf_status_t flush_token (pdf_token_reader_t reader, pdf_u32_t flags,
+ pdf_token_t *token);
+static pdf_status_t handle_char (pdf_token_reader_t reader, pdf_u32_t flags,
+ pdf_char_t ch, pdf_token_t *token);
+static INLINE pdf_status_t handle_string_char (pdf_token_reader_t reader,
+ pdf_u32_t flags,
+ pdf_char_t ch,
+ pdf_token_t *token);
+static INLINE pdf_status_t handle_hexstring_char (pdf_token_reader_t reader,
+ pdf_u32_t flags,
+ pdf_char_t ch,
+ pdf_token_t *token);
+static int recognise_number (pdf_buffer_t buffer, int *int_value);
+static INLINE int parse_integer (pdf_buffer_t buffer, int *int_value,
+ int *int_state);
+static INLINE pdf_status_t parse_real (pdf_buffer_t buffer,
+ char *locale_dec_pt,
+ double *value);
+static INLINE int validate_real (pdf_buffer_t buffer, int int_state);
+
+
+pdf_status_t
+pdf_token_reader_new (pdf_stm_t stm, pdf_token_reader_t *reader)
+{
+ pdf_status_t err;
+ pdf_token_reader_t new_tokr;
+
+ err = PDF_ENOMEM;
+ new_tokr = pdf_alloc (sizeof (*new_tokr));
+ if (!new_tokr)
+ goto fail;
+
+ /* determine the current locale's decimal point
+ * (avoid using localeconv since it may not be thread-safe) */
+ new_tokr->decimal_point = NULL;
+ {
+ int len;
+ char decpt[16];
+
+ err = PDF_ERROR;
+ len = snprintf (decpt, sizeof (decpt), "%#.0f", 1.0);
+ if (len <= 0 || len >= sizeof (decpt)) /* shouldn't happen */
+ goto fail;
+
+ err = PDF_ENOMEM;
+ new_tokr->decimal_point = pdf_alloc (len);
+ if (!new_tokr->decimal_point)
+ goto fail;
+
+ /* this copies the trailing '\0' due to the starting offset */
+ memcpy (new_tokr->decimal_point, &decpt[1], len);
+ }
+
+ /* max string size 32767 + terminating '\0' */
+ new_tokr->buffer = pdf_buffer_new (32768);
+ if (!new_tokr->buffer)
+ goto fail;
+
+ new_tokr->stream = stm;
+ pdf_token_reader_reset (new_tokr);
+
+ *reader = new_tokr;
+ return PDF_OK;
+
+fail:
+ if (new_tokr)
+ {
+ if (new_tokr->decimal_point)
+ pdf_dealloc (new_tokr->decimal_point);
+ pdf_dealloc (new_tokr);
+ }
+
+ return err;
+}
+
+pdf_status_t
+pdf_token_reader_reset (pdf_token_reader_t reader)
+{
+ reader->state = PDF_TOKR_STATE_NONE;
+ reader->substate = 0;
+ return PDF_OK;
+}
+
+pdf_status_t
+pdf_token_reader_destroy (pdf_token_reader_t reader)
+{
+ if (!reader) return PDF_EBADDATA;
+
+ assert (reader->buffer);
+ if (reader->buffer)
+ pdf_buffer_destroy (reader->buffer);
+ pdf_dealloc (reader->decimal_point);
+ pdf_dealloc (reader);
+
+ return PDF_OK;
+}
+
+static INLINE pdf_char_t
+hexval (pdf_char_t ch)
+{
+ if (ch >= 48 && ch <= 48+9) /* '0'--'9' */
+ return ch - 48;
+ if (ch >= 64+1 && ch <= 64+6) /* 'A'--'F' */
+ return ch - (64+1) + 10;
+ if (ch >= 96+1 && ch <= 96+6) /* 'a'--'f' */
+ return ch - (96+1) + 10;
+ return 255;
+}
+
+
+/* Tries to handle the given character and possibly produce a token.
+ * Sets (*token) if a token is produced, and leaves it unmodified otherwise.
+ *
+ * Returns PDF_OK if the character was accepted. Otherwise, an error code
+ * is returned, and the call can be repeated later with the same ch value.
+ * A token may be produced even if the character isn't accepted.
+ */
+static pdf_status_t
+handle_char (pdf_token_reader_t reader, pdf_u32_t flags,
+ pdf_char_t ch, pdf_token_t *token)
+{
+ pdf_status_t rv;
+
+ /* first, handle the states that shouldn't be exited when whitespace
+ * or a delimiter is seen */
+
+ switch (reader->state)
+ {
+ case PDF_TOKR_STATE_EOF:
+ return PDF_EEOF;
+
+ case PDF_TOKR_STATE_STRING:
+ return handle_string_char (reader, flags, ch, token);
+
+ case PDF_TOKR_STATE_HEXSTRING:
+ return handle_hexstring_char (reader, flags, ch, token);
+
+ case PDF_TOKR_STATE_DICTEND:
+ if (ch != 62) /* '>' */
+ return PDF_EBADFILE;
+ reader->substate = 1; /* saw the closing '>' */
+ return exit_state (reader, flags, token);
+
+ case PDF_TOKR_STATE_COMMENT:
+ if (pdf_is_eol_char (ch))
+ {
+ rv = exit_state (reader, flags, token);
+ if (rv != PDF_OK)
+ return rv;
+
+ /* don't accept this character, but process it next time */
+ return PDF_EAGAIN;
+ }
+
+ if (store_char (reader, ch) != PDF_OK)
+ {
+ /* the comment buffer is full, so split the token */
+ rv = flush_token (reader, flags, token);
+ if (rv != PDF_OK)
+ return rv;
+
+ reader->intparam = 1; /* mark the next token as a continuation */
+ return store_char (reader, ch); /* can't fail this time */
+ }
+
+ return PDF_OK;
+
+ default: ;
+ }
+
+ /* now handle delimiters and whitespace */
+
+ if (pdf_is_wspace_char (ch))
+ {
+ if (reader->state)
+ {
+ rv = exit_state (reader, flags, token);
+ if (rv != PDF_OK)
+ return rv;
+
+ /* avoid reading this byte so PDF_TOKEN_END_AT_STREAM
+ * will work properly if it's '\r' */
+ return PDF_EAGAIN;
+ }
+
+ if ((flags & PDF_TOKEN_END_AT_STREAM) && ch == 10) /* LF */
+ {
+ /* found the beginning of a stream */
+ reader->state = PDF_TOKR_STATE_EOF;
+ }
+ return PDF_OK;
+ }
+ else if ((flags & PDF_TOKEN_END_AT_STREAM) && ch != 37) /* 37=='%' */
+ {
+ /* only allow whitespace/comments after the "stream" keyword */
+ return PDF_EBADFILE;
+ }
+
+ if (pdf_is_delim_char (ch))
+ {
+ /* set state 0 (UNINIT), substate 0, bufpos 0 */
+ if (reader->state)
+ {
+ rv = exit_state (reader, flags, token);
+ if (rv != PDF_OK)
+ return rv;
+ return PDF_EAGAIN;
+ }
+
+ switch (ch)
+ {
+ case 37: /* '%' */
+ reader->state = PDF_TOKR_STATE_COMMENT;
+ reader->intparam = 0;
+ return store_char (reader, ch);
+ case 40: /* '(' */
+ reader->state = PDF_TOKR_STATE_STRING;
+ reader->intparam = 0;
+ return PDF_OK;
+ case 41: /* ')' */
+ /* this shouldn't occur outside the STRING and COMMENT states */
+ return PDF_EBADFILE;
+ case 47: /* '/' */
+ reader->state = PDF_TOKR_STATE_NAME;
+ return PDF_OK;
+ case 60: /* '<' */
+ reader->state = PDF_TOKR_STATE_HEXSTRING;
+ return PDF_OK;
+ case 62: /* '>' */
+ reader->state = PDF_TOKR_STATE_DICTEND;
+ return PDF_OK;
+ case 91: /* '[' */
+ /* fall through */
+ case 93: /* ']' */
+ /* fall through */
+ case 123: /* '{' */
+ /* fall through */
+ case 125: /* '}' */
+ /* exit_state may have emitted a token, so we can't emit another
+ * one now; we'll do it when exiting the PENDING state */
+ reader->state = PDF_TOKR_STATE_PENDING;
+ reader->charparam = ch;
+ return PDF_OK;
+ }
+
+ /* not reached (all delimiter chars should be handled) */
+ assert (0);
+ }
+
+ /* ch is a regular character */
+
+ switch (reader->state)
+ {
+ case PDF_TOKR_STATE_PENDING:
+ rv = exit_state (reader, flags, token);
+ if (rv != PDF_OK)
+ return rv;
+ return PDF_EAGAIN;
+
+ case PDF_TOKR_STATE_NONE:
+ reader->state = PDF_TOKR_STATE_KEYWORD;
+ /* fall through */
+
+ case PDF_TOKR_STATE_KEYWORD:
+ return store_char (reader, ch);
+
+ case PDF_TOKR_STATE_NAME:
+ if (reader->substate == 0)
+ {
+ if (ch != 35 /* '#' */
+ || (flags & PDF_TOKEN_NO_NAME_ESCAPES) )
+ return store_char (reader, ch);
+
+ reader->substate = 1;
+ return PDF_OK;
+ }
+
+ if ( (ch = hexval (ch)) >= 16 )
+ return PDF_EBADFILE;
+
+ if (reader->substate == 1) /* the first hex digit of an escape */
+ {
+ reader->substate = 2;
+ reader->charparam = ch;
+ return PDF_OK;
+ }
+
+ ch = (reader->charparam << 4) | ch;
+ if (ch == 0) /* the PDF spec forbids "#00" */
+ return PDF_EBADFILE;
+
+ rv = store_char (reader, ch);
+ if (rv == PDF_OK) reader->substate = 0;
+ return rv;
+
+ default:
+ assert (0);
+ }
+
+ return store_char (reader, ch);
+}
+
+
+static INLINE int
+can_store_char (const pdf_token_reader_t reader)
+{
+ return reader->buffer->wp < (reader->buffer->size - 1);
+}
+
+static INLINE pdf_status_t
+store_char (pdf_token_reader_t reader, pdf_char_t ch)
+{
+ if (!can_store_char (reader))
+ return PDF_EIMPLLIMIT;
+ reader->buffer->data[reader->buffer->wp++] = ch;
+ return PDF_OK;
+}
+
+
+static pdf_status_t
+flush_token (pdf_token_reader_t reader, pdf_u32_t flags, pdf_token_t *token)
+{
+ pdf_status_t rv;
+ pdf_token_t new_tok;
+ pdf_char_t *data = reader->buffer->data;
+ int datasize = reader->buffer->wp;
+
+ switch (reader->state)
+ {
+ case PDF_TOKR_STATE_NONE:
+ return PDF_OK; /* no state to exit */
+
+ case PDF_TOKR_STATE_EOF:
+ return PDF_EEOF; /* can't continue parsing after EOF */
+
+ case PDF_TOKR_STATE_COMMENT:
+ if (!(flags & PDF_TOKEN_RET_COMMENTS))
+ goto finish;
+
+ rv = pdf_token_comment_new (data, datasize,
+ reader->intparam /*continued*/, &new_tok);
+ break;
+
+ case PDF_TOKR_STATE_KEYWORD:
+ {
+ int value;
+ int ntyp = recognise_number (reader->buffer, &value);
+ if (ntyp == 1)
+ rv = pdf_token_integer_new (value, &new_tok);
+ else if (ntyp == 2)
+ {
+ double realvalue;
+ rv = parse_real (reader->buffer,
+ reader->decimal_point,
+ &realvalue);
+ if (rv != PDF_OK)
+ return rv;
+ rv = pdf_token_real_new ((float)realvalue, &new_tok);
+ }
+ else
+ rv = pdf_token_keyword_new (data, datasize, &new_tok);
+ }
+ break;
+
+ case PDF_TOKR_STATE_NAME:
+ if (reader->substate != 0) /* reading an escape sequence */
+ return PDF_EBADFILE;
+
+ rv = pdf_token_name_new (data, datasize, &new_tok);
+ break;
+
+ case PDF_TOKR_STATE_STRING:
+ if (reader->intparam >= 0) /* didn't see the closing ')' */
+ return PDF_EBADFILE;
+
+ rv = pdf_token_string_new (data, datasize, &new_tok);
+ break;
+
+ case PDF_TOKR_STATE_HEXSTRING:
+ if (reader->substate != 3) /* didn't see the closing '>' */
+ return PDF_EBADFILE;
+
+ rv = pdf_token_string_new (data, datasize, &new_tok);
+ break;
+
+ case PDF_TOKR_STATE_DICTEND:
+ if (reader->substate != 1) /* didn't see a second '>' */
+ return PDF_EBADFILE;
+
+ rv = pdf_token_valueless_new (PDF_TOKEN_DICT_END, &new_tok);
+ break;
+
+ case PDF_TOKR_STATE_PENDING:
+ switch (reader->charparam)
+ {
+ case 60: /* '<' */
+ rv = pdf_token_valueless_new (PDF_TOKEN_DICT_START, &new_tok);
+ break;
+ case 91: /* '[' */
+ rv = pdf_token_valueless_new (PDF_TOKEN_ARRAY_START, &new_tok);
+ break;
+ case 93: /* ']' */
+ rv = pdf_token_valueless_new (PDF_TOKEN_ARRAY_END, &new_tok);
+ break;
+ case 123: /* '{' */
+ rv = pdf_token_valueless_new (PDF_TOKEN_PROC_START, &new_tok);
+ break;
+ case 125: /* '}' */
+ rv = pdf_token_valueless_new (PDF_TOKEN_PROC_END, &new_tok);
+ break;
+ default:
+ assert (0);
+ return PDF_ERROR;
+ }
+ break;
+
+ default:
+ assert (0);
+ return PDF_ERROR;
+ }
+
+ if (rv != PDF_OK)
+ return rv;
+
+ *token = new_tok;
+finish:
+ reader->buffer->wp = 0;
+ return PDF_OK;
+}
+
+
+static pdf_status_t
+exit_state (pdf_token_reader_t reader, pdf_u32_t flags, pdf_token_t *token)
+{
+ pdf_status_t rv = flush_token (reader, flags, token);
+ if (rv == PDF_OK)
+ {
+ reader->state = PDF_TOKR_STATE_NONE;
+ reader->substate = 0;
+ }
+ return rv;
+}
+
+
+static INLINE pdf_status_t
+handle_string_char (pdf_token_reader_t reader,
+ pdf_u32_t flags,
+ pdf_char_t ch,
+ pdf_token_t *token)
+{
+ pdf_status_t rv;
+start:
+ switch (reader->substate)
+ {
+ case 1: /* ignore LF */
+ reader->substate = 0;
+ if (ch == 10)
+ return PDF_OK;
+ /* fall through */
+
+ case 0: /* no special state */
+ if (ch == 92) /* '\\' */
+ {
+ reader->substate = 2; /* start an escape sequence */
+ return PDF_OK;
+ }
+ else if (ch == 41 && reader->intparam <= 0) /* ')'; end of string */
+ {
+ reader->intparam = -1;
+ return exit_state (reader, flags, token);
+ }
+
+ if (!can_store_char (reader))
+ return PDF_EIMPLLIMIT;
+ else if (ch == 40) /* '(' */
+ ++reader->intparam;
+ else if (ch == 41) /* ')' */
+ --reader->intparam;
+ else if (ch == 13) /* '\r' */
+ {
+ ch = 10; /* treat as LF */
+ reader->substate = 1; /* ignore the next char if it's LF */
+ }
+
+ return store_char (reader, ch);
+
+ case 2: /* just saw a '\\' (starting an escape sequence) */
+ reader->substate = 0;
+ if (ch == 98) /* 'b' */
+ ch = 8; /* BS: backspace */
+ else if (ch == 102) /* 'f' */
+ ch = 12; /* FF: formfeed */
+ else if (ch == 110) /* 'n' */
+ ch = 10; /* NL: newline */
+ else if (ch == 114) /* 'r' */
+ ch = 13; /* CR: carriage return */
+ else if (ch == 116) /* 't' */
+ ch = 9; /* HT: horizontal tab */
+ else if (ch == 10) /* NL */
+ return PDF_OK; /* ignore the line break */
+ else if (ch == 13) /* CR */
+ {
+ /* ignore the line break; also ignore the next byte if it's LF */
+ reader->substate = 1;
+ return PDF_OK;
+ }
+ else if (ch >= 48 && ch <= 48+7) /* digits '0'--'7' */
+ {
+ /* starting an octal escape; we'll read three digits even if the
+ * first is '4'--'7' (and calculate the final char modulo 256),
+ * since the PDF/PS specs say to ignore high-order overflow */
+ reader->substate = 3;
+ reader->charparam = (ch-48);
+ return PDF_OK;
+ }
+
+ /* for any other character, including '(', ')', and '\\',
+ * store the same character (dropping the leading backslash) */
+ return store_char (reader, ch);
+
+ case 3: /* saw 1 digit of an octal escape */
+ /* fall through */
+ case 4: /* saw 2 digits of an octal escape */
+ if (ch < 48 || ch > 48+7) /* not digits '0'--'7' */
+ {
+ rv = store_char (reader, reader->charparam);
+ if (rv != PDF_OK) return rv;
+
+ /* ch isn't part of the escape sequence, so retry */
+ reader->substate = 0;
+ goto start;
+ }
+
+ /* ch is a digit from '0'--'7' */
+ reader->charparam = ((reader->charparam & 0x1f) << 3) | (ch - 48);
+ if (reader->substate == 4) /* this was the final digit */
+ {
+ rv = store_char (reader, reader->charparam);
+ if (rv != PDF_OK) return rv;
+
+ reader->substate = 0;
+ return PDF_OK;
+ }
+
+ reader->substate = 4;
+ return PDF_OK;
+
+ default:
+ assert (0);
+ }
+}
+
+
+static INLINE pdf_status_t
+handle_hexstring_char (pdf_token_reader_t reader,
+ pdf_u32_t flags,
+ pdf_char_t ch,
+ pdf_token_t *token)
+{
+ pdf_status_t rv;
+
+ if (reader->substate == 0)
+ {
+ /* this is the first character after the initial '<' */
+ if (ch == 60) /* '<' */
+ {
+ /* this was actually the start of a dictionary */
+ reader->state = PDF_TOKR_STATE_PENDING;
+ reader->charparam = ch;
+ return exit_state (reader, flags, token);
+ }
+
+ reader->substate = 1;
+ }
+
+ if (pdf_is_wspace_char (ch))
+ return PDF_OK;
+
+ if (ch == 62) /* '>': end of hex string */
+ {
+ if (reader->substate == 2)
+ {
+ /* the last digit is missing; assume it's '0' */
+ rv = store_char (reader, reader->charparam << 4);
+ if (rv != PDF_OK) return rv;
+ }
+
+ reader->substate = 3; /* saw end of string */
+ return exit_state (reader, flags, token);
+ }
+
+ if ( (ch = hexval (ch)) == 255 )
+ return PDF_EBADFILE;
+
+ if (reader->substate == 1) /* first character in a pair */
+ {
+ reader->substate = 2;
+ reader->charparam = ch;
+ return PDF_OK;
+ }
+
+ rv = store_char (reader, (reader->charparam << 4) | ch);
+ if (rv == PDF_OK)
+ reader->substate = 1;
+ return rv;
+}
+
+pdf_status_t
+pdf_token_read (pdf_token_reader_t reader, pdf_u32_t flags, pdf_token_t *token)
+{
+ pdf_status_t rv;
+ pdf_char_t ch;
+ pdf_token_t new_token = NULL;
+
+ if (!reader || !reader->stream || !token)
+ return PDF_EBADDATA;
+
+ while ( (rv = pdf_stm_peek_char (reader->stream, &ch)) == PDF_OK )
+ {
+ rv = handle_char (reader, flags, ch, &new_token);
+ if (rv == PDF_OK)
+ {
+ /* The character we peeked at was accepted, so get rid of it. */
+ pdf_stm_read_char (reader->stream, &ch);
+ }
+
+ if (new_token)
+ {
+ /* Don't return an error code if we got a valid token.
+ * We'll probably see the same error on the next call since we
+ * didn't call read_char. */
+ assert (rv == PDF_OK || rv == PDF_EAGAIN);
+ goto ret_token;
+ }
+ else if (rv != PDF_OK && rv != PDF_EAGAIN)
+ return rv;
+ }
+
+ /* peek_char returned an error code (rv) */
+ if (rv != PDF_EEOF)
+ return rv;
+
+ rv = exit_state (reader, flags, &new_token);
+ if (rv != PDF_OK)
+ return rv;
+
+ reader->state = PDF_TOKR_STATE_EOF;
+ if (new_token)
+ goto ret_token;
+ else
+ return PDF_EEOF;
+
+ret_token:
+ assert (new_token);
+ *token = new_token;
+ return PDF_OK;
+}
+
+
+static INLINE int
+parse_integer (pdf_buffer_t buffer, int *int_value, int *int_state)
+{
+ /* Parse an ASCII integer with the given radix, at the beginning of
+ * the buffer (possibly leaving unread bytes at the end).
+ *
+ * Return value is 0 on failure, or a bitmask otherwise:
+ * 1 = valid integer
+ * 2 = signed
+ * 4 = overflowed (no value stored in *int_value)
+ */
+
+ int sign = 0, tmpint = 0, overflowed = 0, ret;
+ /* Integer states (int_state):
+ * 0 = at start (looking for sign or digits)
+ * 1 = saw sign
+ * 2 = saw digits
+ */
+
+ *int_state = 0;
+ for (; buffer->rp < buffer->wp; ++buffer->rp)
+ {
+ int chval;
+ pdf_char_t ch = buffer->data[buffer->rp];
+ if (ch == 43 || ch == 45) /* '+','-' */
+ {
+ if (*int_state != 0)
+ goto out;
+
+ *int_state = 1;
+ sign = (ch == 43) ? 1 : -1;
+ continue;
+ }
+
+ chval = ch - 48; /* assume this is a digit */
+ if (chval < 0 || chval > 9)
+ goto out; /* not a valid number */
+
+ *int_state = 2;
+ if (overflowed)
+ continue;
+
+ /* convert the digits to an integer, if possible */
+ if (sign < 0)
+ {
+ chval = -chval;
+ if ( tmpint < (INT_MIN/10)
+ || (tmpint == (INT_MIN/10) && chval < (INT_MIN%10)) )
+ {
+ overflowed = 1; /* would overflow */
+ continue;
+ }
+ }
+ else
+ {
+ if ( tmpint > (INT_MAX/10)
+ || (tmpint == (INT_MAX/10) && chval > (INT_MAX%10)) )
+ {
+ overflowed = 1; /* would overflow */
+ continue;
+ }
+ }
+
+ tmpint += chval + (tmpint * 9);
+ }
+
+out:
+ if (*int_state != 2)
+ return 0; /* never saw any digits */
+
+ ret = 1;
+ if (sign) ret += 2;
+ if (overflowed)
+ ret += 4;
+ else
+ *int_value = tmpint;
+
+ return ret;
+}
+
+
+static INLINE int
+validate_real (pdf_buffer_t buffer, int int_state)
+{
+ /* Determines whether the given number is a valid PS/PDF real number;
+ * assumes the initial sign was already read (if present), and any data
+ * before buffer->rp is a valid integer.
+ *
+ * Return value:
+ * 0 = not a real number
+ * 1 = valid PDF/PS real
+ */
+
+ int seen_point = 0;
+ /* Integer states (int_state):
+ * 0 = at start
+ * 1 = saw sign
+ * 2 = saw digits
+ */
+
+ for (; buffer->rp < buffer->wp; ++buffer->rp)
+ {
+ pdf_char_t ch = buffer->data[buffer->rp];
+ if (ch == 46) /* '.' */
+ {
+ if (!seen_point)
+ seen_point = 1;
+ else
+ return 0;
+ }
+ else if (ch == 43 || ch == 45) /* '+','-' */
+ {
+ if (int_state == 0)
+ int_state = 1;
+ else
+ return 0;
+ }
+ else if (ch >= 48+0 && ch <= 48+9)
+ int_state = 2;
+ else
+ return 0;
+ }
+
+ return (int_state == 2); /* only valid if we saw a digit */
+}
+
+
+/* Given a buffer containing a validated PDF real (in ASCII), convert it to a
+ * double by translating it to the execution character set, replacing '.' with
+ * the locale's decimal point, and calling strtod. */
+static INLINE pdf_status_t
+parse_real (pdf_buffer_t buffer, char *locale_dec_pt, double *value)
+{
+ pdf_status_t ret;
+ size_t tmplen, wpos, ptlen;
+ char *tmp, *endptr;
+
+ ptlen = strlen (locale_dec_pt);
+ /* we may remove 1 byte ('.') and replace it with ptlen bytes */
+ tmplen = buffer->wp - 1 + ptlen;
+
+ tmp = pdf_alloc (tmplen + 1);
+ if (!tmp)
+ return PDF_ENOMEM;
+
+ wpos = 0;
+ ret = PDF_ERROR; /* nothing should fail if the buffer was validated */
+ for (buffer->rp = 0; buffer->rp < buffer->wp; ++buffer->rp)
+ {
+ pdf_char_t ch = buffer->data[buffer->rp];
+ if (wpos >= tmplen)
+ goto out;
+
+ if (ch == 46) /* '.' */
+ {
+ if (wpos + ptlen > tmplen)
+ goto out;
+
+ memcpy (tmp + wpos, locale_dec_pt, ptlen);
+ wpos += ptlen;
+ }
+ else if (ch == 43) /* '+' */
+ tmp[wpos++] = '+';
+ else if (ch == 45) /* '-' */
+ tmp[wpos++] = '-';
+ else if (ch >= 48+0 && ch <= 48+9) /* '0'--'9' */
+ tmp[wpos++] = '0' + (ch-48);
+ else
+ goto out;
+ }
+
+ /* null-terminate the new string, and call strtod to get its value
+ * (strtof would also work if it's available) */
+ tmp[wpos] = '\0';
+ *value = strtod (tmp, &endptr);
+ if (endptr == tmp + wpos)
+ ret = PDF_OK;
+
+out:
+ pdf_dealloc (tmp);
+ return ret;
+}
+
+
+/*
+ * Return value:
+ * 0 = not a number
+ * 1 = integer (stored in *int_value)
+ * 2 = real
+ */
+static int
+recognise_number (pdf_buffer_t buffer, int *int_value)
+{
+ int rv, tmpint = 0, int_state = 0;
+
+ /* try to parse as an integer */
+
+ buffer->rp = 0;
+ rv = parse_integer (buffer, &tmpint, &int_state);
+
+ if (buffer->rp < buffer->wp) /* didn't look at the whole buffer */
+ {
+ rv = validate_real (buffer, int_state);
+ if (rv == 1)
+ return 2;
+
+ return 0;
+ }
+
+ if (!rv) return 0;
+ else if (rv & 4)
+ return 2; /* integer overflowed, but could be read as a real */
+
+ *int_value = tmpint;
+ return 1;
+}
+
+
+/* End of pdf-token-reader.c */
=== added file 'src/base/pdf-token-reader.h'
--- src/base/pdf-token-reader.h 1970-01-01 00:00:00 +0000
+++ src/base/pdf-token-reader.h 2009-05-20 05:28:26 +0000
@@ -0,0 +1,124 @@
+/* -*- mode: C -*- Time-stamp: "2009-05-20 04:01:14 mgold"
+ *
+ * File: pdf-token-reader.h
+ * Date: Mon Dec 29 00:45:09 2008
+ *
+ * GNU PDF Library - Stream tokeniser
+ *
+ */
+
+/* Copyright (C) 2008, 2009 Free Software Foundation, Inc. */
+
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PDF_TOKEN_READER_H
+#define PDF_TOKEN_READER_H
+#include "pdf-types.h"
+#include "pdf-stm.h"
+#include "pdf-token-obj.h"
+
+/* BEGIN PUBLIC */
+/* pdf-token-reader.h */
+
+struct pdf_token_reader_s; /* opaque type */
+typedef struct pdf_token_reader_s *pdf_token_reader_t;
+
+pdf_status_t pdf_token_reader_new(pdf_stm_t stm, pdf_token_reader_t *reader);
+pdf_status_t pdf_token_reader_destroy(pdf_token_reader_t reader);
+pdf_status_t pdf_token_reader_reset(pdf_token_reader_t reader);
+pdf_status_t pdf_token_read(pdf_token_reader_t reader, pdf_u32_t flags,
+ pdf_token_t *token);
+
+enum pdf_token_rw_flags_e {
+ /* these flags are used for token_read and token_write */
+ PDF_TOKEN_NO_NAME_ESCAPES = 1, /* read/write */
+ PDF_TOKEN_RET_COMMENTS = 2, /* read */
+ PDF_TOKEN_END_AT_STREAM = 4, /* read */
+ PDF_TOKEN_HEX_STRINGS = 8, /* write */
+ PDF_TOKEN_READABLE_STRINGS = 16, /* write */
+};
+
+/* END PUBLIC */
+
+enum pdf_token_reader_state_e
+{
+ /* PDF_TOKR_ is used as an internal prefix for the token reader. */
+ PDF_TOKR_STATE_NONE = 0,
+ PDF_TOKR_STATE_COMMENT,
+ PDF_TOKR_STATE_KEYWORD,
+ PDF_TOKR_STATE_NAME,
+ PDF_TOKR_STATE_STRING,
+ PDF_TOKR_STATE_HEXSTRING,
+ PDF_TOKR_STATE_DICTEND,
+ PDF_TOKR_STATE_PENDING,
+ PDF_TOKR_STATE_EOF
+};
+
+/* Token reader states (from pdf_token_reader_state_e):
+ * NONE - Initial state; not reading a token.
+ * COMMENT - Reading a comment. buffer collects the comment bytes, excluding
+ * intparam is 1 if this token is continued from a previous token,
+ * and 0 otherwise.
+ * KEYWORD - Reading some regular characters into buffer; this could result
+ * in a symbol like "null", or a number.
+ * NAME - Reading a name (which starts with '/').
+ * Substates:
+ * 0 - normal state
+ * 1 - just read a '#' (escape prefix)
+ * 2 - read the first hex digit after '#'; the value is in charparam
+ * buffer collects the name, excluding the initial '/'.
+ * STRING - Reading a literal string (enclosed in '(', ')').
+ * Substates:
+ * 0 - normal state
+ * 1 - ignore the next byte if its value is 10 (ASCII LF;
+ * this is used to treat CRLF as a single line ending)
+ * 2 - just saw a backslash (escape prefix)
+ * 3 - read 1 octal digit; the value is in charparam
+ * 4 - read 2 octal digits; the value is in charparam
+ * intparam is the bracket nesting level; ')' at level 0 ends the string.
+ * buffer collects the string.
+ * HEXSTRING - Reading a hex string.
+ * Substates:
+ * 0 - initial state: we just saw the opening '<', and if the next byte is
+ * also '<' this is the start of a dictionary rather than a string
+ * 1 - normal state (the next hex digit will be the first in a pair)
+ * 2 - read the first hex digit; its value is in charparam
+ * 3 - end state; saw the closing '>'
+ * buffer collects the string.
+ * DICTEND - Just got a '>'; expecting another.
+ * Substates:
+ * 0 - starting state
+ * 1 - saw the second '>'
+ * PENDING - Need to emit a token (determined by charparam) ASAP.
+ * EOF - Can't continue tokenising (reached EOF, or beginning of stream)
+ */
+
+/* Internal state */
+struct pdf_token_reader_s {
+ pdf_stm_t stream; /* stream to read bytes from */
+ char *decimal_point;
+
+ /* variables used by the state machine (described above) */
+ int state;
+ int substate;
+ pdf_char_t charparam;
+ int intparam;
+ pdf_buffer_t buffer;
+ /***/
+};
+
+#endif
+
+/* End of pdf-token-reader.h */
=== modified file 'torture/unit/Makefile.am'
--- torture/unit/Makefile.am 2009-05-13 16:30:24 +0000
+++ torture/unit/Makefile.am 2009-05-20 04:01:14 +0000
@@ -209,29 +209,33 @@
TEST_SUITE_FP = base/fp/pdf-fp-func-eval.c \
base/fp/pdf-fp-func-4-new.c
+TEST_SUITE_TOKEN = base/token/pdf-token-read.c
+
TEST_ENVIRONMENT = CHARSETALIASDIR=$(top_srcdir)/lib
TEST_FILES = $(TEST_SUITE_LIST) \
$(TEST_SUITE_TEXT) \
$(TEST_SUITE_HASH) \
$(TEST_SUITE_ERROR) \
- $(TEST_SUITE_TYPES) \
- $(TEST_SUITE_TIME) \
+ $(TEST_SUITE_TYPES) \
+ $(TEST_SUITE_TIME) \
$(TEST_SUITE_CRYPT) \
$(TEST_SUITE_ALLOC) \
$(TEST_SUITE_STM) \
- $(TEST_SUITE_FP)
+ $(TEST_SUITE_FP) \
+ $(TEST_SUITE_TOKEN)
TSUITE_FILES = base/alloc/tsuite-alloc.c \
base/list/tsuite-list.c \
base/text/tsuite-text.c \
base/hash/tsuite-hash.c \
base/error/tsuite-error.c \
- base/types/tsuite-types.c \
- base/time/tsuite-time.c \
+ base/types/tsuite-types.c \
+ base/time/tsuite-time.c \
base/crypt/tsuite-crypt.c \
base/stm/tsuite-stm.c \
- base/fp/tsuite-fp.c
+ base/fp/tsuite-fp.c \
+ base/token/tsuite-token.c
TORTUTILS_FILES = $(top_srcdir)/torture/tortutils/tortutils.h \
$(top_srcdir)/torture/tortutils/tortutils.c
=== added directory 'torture/unit/base/token'
=== added file 'torture/unit/base/token/pdf-token-read.c'
--- torture/unit/base/token/pdf-token-read.c 1970-01-01 00:00:00 +0000
+++ torture/unit/base/token/pdf-token-read.c 2009-05-20 04:55:54 +0000
@@ -0,0 +1,159 @@
+/* -*- mode: C -*- Time-stamp: "2009-05-20 04:35:27 mgold"
+ *
+ * File: pdf-token-read.c
+ * Date: Wed Jan 14 05:44:48 2009
+ *
+ * GNU PDF Library - Unit tests for pdf_token_read
+ *
+ */
+
+#include <string.h>
+#include <check.h>
+#include <stdlib.h>
+#include "config.h"
+#include "pdf.h"
+
+#define STR_AND_LEN(s) (s),(sizeof(s)-1)
+
+/* Initialize an in-memory reader stream (pdf_stm_t *stm)
+ * with the given string (a char* constant). */
+#define INIT_STM_STR(stm,str) do{ \
+ fail_unless(PDF_OK==pdf_stm_mem_new( STR_AND_LEN(str), \
+ 0 /*cache_size*/, PDF_STM_READ /*mode*/, (stm) )); \
+ }while(0)
+
+#define INIT_TOKR(tokr,stm) do{ fail_unless( \
+ PDF_OK==pdf_token_reader_new((stm),(tokr)) ); }while(0)
+#define _EXPECT_TOK(tokr,flags,tokexpr) do{ \
+ pdf_token_t _exp_tok; \
+ fail_unless(PDF_OK == (tokexpr)); \
+ fail_unless(get_token( (tokr), (flags), _exp_tok )); \
+ pdf_token_destroy(_exp_tok); \
+ }while(0)
+#define EXPECT_VALUELESS(tokr,flags,val) _EXPECT_TOK((tokr), \
+ (flags), pdf_token_valueless_new(val, &_exp_tok) )
+#define EXPECT_KEYWORD(tokr,flags,val) _EXPECT_TOK((tokr), \
+ (flags), pdf_token_keyword_new(STR_AND_LEN(val), &_exp_tok) )
+#define EXPECT_NAME(tokr,flags,val) _EXPECT_TOK((tokr), \
+ (flags), pdf_token_name_new(STR_AND_LEN(val), &_exp_tok) )
+#define EXPECT_INTEGER(tokr,flags,val) _EXPECT_TOK((tokr), \
+ (flags), pdf_token_integer_new(val, &_exp_tok) )
+#define EXPECT_REAL(tokr,flags,val) _EXPECT_TOK((tokr), \
+ (flags), pdf_token_real_new(val, &_exp_tok) )
+#define EXPECT_STRING(tokr,flags,val) _EXPECT_TOK((tokr), \
+ (flags), pdf_token_string_new(STR_AND_LEN(val), &_exp_tok) )
+
+static void *nonnull(void *ptr)
+{
+ if (ptr) return ptr;
+
+ fprintf(stderr, "error: got unexpected null pointer; aborting");
+ abort();
+}
+
+/* Read a token; return 1 if it's equal to 'expected', or 0 otherwise. */
+static int
+get_token(pdf_token_reader_t tokr, pdf_u32_t flags, pdf_token_t expected)
+{
+ int ret = 0;
+ pdf_token_t token = NULL;
+ pdf_status_t rv = pdf_token_read(tokr, flags, &token);
+
+ nonnull(expected);
+ ret = (rv == PDF_OK) && pdf_token_equal_p(token, expected);
+
+ if (token) pdf_token_destroy(token);
+ return ret;
+}
+
+static int
+tokr_eof(pdf_token_reader_t tokr, pdf_i32_t flags)
+{
+ pdf_token_t token = NULL;
+ pdf_status_t rv = pdf_token_read(tokr, flags, &token);
+ if (token) pdf_token_destroy(token);
+ return (rv == PDF_EEOF);
+}
+
+/*
+ * Test: pdf_token_read_001
+ * Description:
+ * Read various tokens from an in-memory stream, and check whether they
+ * match the expected values.
+ * Success condition:
+ * Each token matches the expected one (according to pdf_token_equal_p),
+ * and no tokens remain afterwards.
+ */
+START_TEST(pdf_token_read_001)
+{
+ pdf_stm_t stm;
+ pdf_token_reader_t tokr;
+
+ INIT_STM_STR(&stm, "abc}{/kw/k#20w%com\n"
+ "]1 2.0[>><4142434a4F4>"
+ "(str(\\551ng(\0\r)\\60x\\\r\n)>>)"
+ "<<1208925819614629174706176" /* 2^80 */
+ "\0\n \r\t\f" /* whitespace */
+ "(\\n\\r\\t\\b\\f\\)\\(\\\\\\X\08)"); /* string escapes */
+ INIT_TOKR(&tokr, stm);
+
+ EXPECT_KEYWORD( tokr, 0, "abc" );
+ EXPECT_VALUELESS( tokr, 0, PDF_TOKEN_PROC_END );
+ EXPECT_VALUELESS( tokr, 0, PDF_TOKEN_PROC_START );
+ EXPECT_NAME( tokr, 0, "kw" );
+ EXPECT_NAME( tokr, 0, "k w" );
+ EXPECT_VALUELESS( tokr, 0, PDF_TOKEN_ARRAY_END );
+ EXPECT_INTEGER( tokr, 0, 1 );
+ EXPECT_REAL( tokr, 0, 2 );
+ EXPECT_VALUELESS( tokr, 0, PDF_TOKEN_ARRAY_START );
+ EXPECT_VALUELESS( tokr, 0, PDF_TOKEN_DICT_END );
+ EXPECT_STRING( tokr, 0, "ABCJO@" );
+ EXPECT_STRING( tokr, 0, "str(ing(\0\n)0x)>>" );
+ EXPECT_VALUELESS( tokr, 0, PDF_TOKEN_DICT_START );
+ EXPECT_REAL( tokr, 0, 1208925819614629174706176.0 ); /* 2^80 */
+ EXPECT_STRING( tokr, 0, "\n\r\t\b\f)(\\X\0\070" );
+
+ fail_unless( tokr_eof(tokr, 0) );
+}
+END_TEST
+
+/*
+ * Test: pdf_token_read_002
+ * Description:
+ * Test the PDF_TOKEN_END_AT_STREAM flag.
+ * Success condition:
+ * The stream should be positioned after the '\n' character (at '<'), and
+ * the token reader should act as if it reached the end of the input file.
+ */
+START_TEST(pdf_token_read_002)
+{
+ pdf_stm_t stm;
+ pdf_token_reader_t tokr;
+ pdf_char_t ch;
+
+ INIT_STM_STR(&stm, "stream \r\t %com \n"
+ ">UNPARSABLE DATA IN STREAM\n\n\n\n\n\nx");
+ INIT_TOKR(&tokr, stm);
+
+ EXPECT_KEYWORD( tokr, 0, "stream" );
+ fail_unless( tokr_eof(tokr, PDF_TOKEN_END_AT_STREAM) );
+ fail_unless( PDF_OK==pdf_stm_peek_char(stm, &ch) );
+ fail_unless( ch == '>' );
+ /*TODO: verify stream position */
+}
+END_TEST
+
+/*
+ * Test case creation function
+ */
+TCase *
+test_pdf_token_read (void)
+{
+ TCase *tc = tcase_create("pdf_token_read");
+ tcase_add_test(tc, pdf_token_read_001);
+ tcase_add_test(tc, pdf_token_read_002);
+
+ return tc;
+}
+
+/* End of pdf-token-read.c */
=== added file 'torture/unit/base/token/tsuite-token.c'
--- torture/unit/base/token/tsuite-token.c 1970-01-01 00:00:00 +0000
+++ torture/unit/base/token/tsuite-token.c 2009-05-20 05:28:26 +0000
@@ -0,0 +1,43 @@
+/* -*- mode: C -*- Time-stamp: "2009-05-20 04:35:27 mgold"
+ *
+ * File: tsuite-token.c
+ * Date: Wed Jan 14 05:43:09 2009
+ *
+ * GNU PDF Library - Testcase definition for the reader module
+ *
+ */
+
+/* Copyright (C) 2009 Free Software Foundation, Inc. */
+
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <check.h>
+
+extern TCase *test_pdf_token_read (void);
+
+Suite *
+tsuite_token ()
+{
+ Suite *s;
+
+ s = suite_create("token");
+
+ suite_add_tcase (s, test_pdf_token_read ());
+
+ return s;
+}
+
+
+/* End of tsuite-token.c */
=== added directory 'torture/unit/object'
=== modified file 'torture/unit/runtests.c'
--- torture/unit/runtests.c 2009-04-16 22:13:38 +0000
+++ torture/unit/runtests.c 2009-05-20 04:01:14 +0000
@@ -19,6 +19,7 @@
extern Suite *tsuite_crypt (void);
extern Suite *tsuite_error (void);
extern Suite *tsuite_fp (void);
+extern Suite *tsuite_token (void);
int
main (int argc, char **argv)
@@ -39,6 +40,7 @@
srunner_add_suite (sr, tsuite_error ());
srunner_add_suite (sr, tsuite_stm ());
srunner_add_suite (sr, tsuite_fp ());
+ srunner_add_suite (sr, tsuite_token ());
/* Set log file */
srunner_set_log (sr, "ut.log");
=== modified file 'utils/Makefile.am'
--- utils/Makefile.am 2009-01-01 17:00:46 +0000
+++ utils/Makefile.am 2009-05-20 04:01:14 +0000
@@ -22,7 +22,7 @@
ICONV_LIBS = -liconv
endif #ICONV
-bin_PROGRAMS = pdf-filter
+bin_PROGRAMS = pdf-filter toktest
LDADD = $(top_builddir)/src/libgnupdf.la \
$(ICONV_LIBS)
@@ -31,5 +31,6 @@
-I$(top_srcdir)/src/base -I $(top_srcdir)/src/object
pdf_filter_SOURCES = pdf-filter.h pdf-filter.c
+toktest_SOURCES = toktest.c
# End of Makefile.am
=== added file 'utils/toktest.c'
--- utils/toktest.c 1970-01-01 00:00:00 +0000
+++ utils/toktest.c 2009-05-20 05:28:26 +0000
@@ -0,0 +1,195 @@
+/* -*- mode: C -*- Time-stamp: "2009-05-20 05:25:40 mgold"
+ *
+ * File: toktest.c
+ * Date: Wed May 20 05:25:40 2009
+ *
+ * GNU PDF Library - Read standard input using a token reader,
+ * and print the resulting tokens to stdout.
+ */
+
+/* Copyright (C) 2009 Free Software Foundation, Inc. */
+
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "pdf.h"
+
+static char *fmtbin(const char *data, int size)
+{
+ int i;
+ char *ret, *retp;
+
+ if (size == -1)
+ size = strlen(data);
+
+ ret = malloc(3 + (size*4));
+ if (!ret) abort();
+
+ retp = ret;
+ (*retp++) = '"';
+ for (i = 0; i < size; ++i)
+ {
+ unsigned char ch = data[i];
+ if (ch == '"' || ch == '\\')
+ {
+ *(retp++) = '\\';
+ *(retp++) = ch;
+ }
+ else if (data[i] >= 32 && data[i] < 127)
+ *(retp++) = ch;
+ else
+ {
+ sprintf(retp, "\\x%02x", (int)ch);
+ retp += 4;
+ }
+ }
+ (*retp++) = '"';
+ (*retp++) = '\0';
+ return ret;
+}
+
+static void print_tok(pdf_token_t token)
+{
+ char tmpbuf[256];
+ const char *typ, *str = NULL;
+ int strsize = -1;
+ pdf_size_t i;
+
+ switch(pdf_token_get_type(token))
+ {
+ case PDF_TOKEN_INTEGER:
+ typ = "INTEGER";
+ snprintf(tmpbuf, sizeof(tmpbuf), "%d",
pdf_token_get_integer_value(token));
+ str = tmpbuf;
+ break;
+
+ case PDF_TOKEN_REAL:
+ typ = "REAL";
+ snprintf(tmpbuf, sizeof(tmpbuf), "%f", pdf_token_get_real_value(token));
+ str = tmpbuf;
+ break;
+
+ case PDF_TOKEN_STRING:
+ typ = "STRING";
+ str = pdf_token_get_string_data(token);
+ strsize = pdf_token_get_string_size(token);
+ break;
+
+ case PDF_TOKEN_NAME:
+ typ = "NAME";
+ str = pdf_token_get_name_data(token);
+ strsize = pdf_token_get_name_size(token);
+ break;
+
+ case PDF_TOKEN_COMMENT:
+ typ = "COMMENT";
+ str = pdf_token_get_comment_data(token);
+ strsize = pdf_token_get_comment_size(token);
+ break;
+
+ case PDF_TOKEN_KEYWORD:
+ typ = "KEYWORD";
+ str = pdf_token_get_keyword_data(token);
+ strsize = pdf_token_get_keyword_size(token);
+ break;
+
+ case PDF_TOKEN_DICT_START:
+ typ = "DICT_START";
+ break;
+ case PDF_TOKEN_DICT_END:
+ typ = "DICT_END";
+ break;
+ case PDF_TOKEN_ARRAY_START:
+ typ = "ARRAY_START";
+ break;
+ case PDF_TOKEN_ARRAY_END:
+ typ = "ARRAY_END";
+ break;
+ case PDF_TOKEN_PROC_START:
+ typ = "PROC_START";
+ break;
+ case PDF_TOKEN_PROC_END:
+ typ = "PROC_END";
+ break;
+
+ default:
+ typ = "[unknown]";
+ sprintf(tmpbuf, "%d", pdf_token_get_type(token));
+ str = tmpbuf;
+ }
+
+ if (str == NULL)
+ {
+ tmpbuf[0] = '\0';
+ str = tmpbuf;
+ }
+ if (str != tmpbuf) str = fmtbin(str, strsize);
+ printf("%s(%s)\n", typ, str);
+ if (str != tmpbuf)
+ {
+ free((void*)str);
+ }
+};
+
+void print_file(FILE *file)
+{
+ pdf_status_t rv;
+ pdf_token_reader_t reader = NULL;
+ pdf_token_t token;
+ pdf_stm_t stm = NULL;
+
+ rv = pdf_stm_cfile_new (file, 0, 0 /*cache_size*/, PDF_STM_READ, &stm);
+ if (rv != PDF_OK)
+ {
+ fprintf(stderr, "failed to create stream\n");
+ goto out;
+ }
+
+ rv = pdf_token_reader_new(stm, &reader);
+ if (rv != PDF_OK)
+ {
+ fprintf(stderr, "failed to create reader\n");
+ goto out;
+ }
+
+ while (( rv = pdf_token_read(reader, 0, &token) ) == PDF_OK)
+ {
+ print_tok(token);
+ pdf_token_destroy(token);
+ }
+
+ if (rv != PDF_EEOF)
+ {
+ fprintf(stderr, "read_token error %d\n", rv);
+ goto out;
+ }
+
+ fprintf(stderr, "done\n");
+out:
+ if (reader) pdf_token_reader_destroy(reader);
+ if (stm) pdf_stm_destroy(stm);
+}
+
+int main(int argc, char **argv)
+{
+ setlocale(LC_ALL, "");
+ print_file(stdin);
+ return 0;
+}
# Begin bundle
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
Pis5jY/+C7kinChIeWGcjIA=
signature.asc
Description: Digital signature
- [pdf-devel] [PATCH] Token reader, with new API,
Michael Gold <=