Update date/time parsing (Issue #115)

This commit is contained in:
Michael R Sweet 2025-04-04 19:12:16 -04:00
parent b8ea9ea064
commit 8cca645835
No known key found for this signature in database
GPG Key ID: BE67C75EC81F3244
4 changed files with 241 additions and 76 deletions

View File

@ -5,6 +5,7 @@ Changes in PDFio
v1.5.2 - YYYY-MM-DD v1.5.2 - YYYY-MM-DD
------------------- -------------------
- Fixed parsing of certain date/time values (Issue #115)
- Fixed support for empty name values (Issue #116) - Fixed support for empty name values (Issue #116)

203
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for pdfio 1.5.1. # Generated by GNU Autoconf 2.71 for pdfio 1.5.2.
# #
# Report bugs to <https://github.com/michaelrsweet/pdfio/issues>. # Report bugs to <https://github.com/michaelrsweet/pdfio/issues>.
# #
@ -610,8 +610,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='pdfio' PACKAGE_NAME='pdfio'
PACKAGE_TARNAME='pdfio' PACKAGE_TARNAME='pdfio'
PACKAGE_VERSION='1.5.1' PACKAGE_VERSION='1.5.2'
PACKAGE_STRING='pdfio 1.5.1' PACKAGE_STRING='pdfio 1.5.2'
PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues' PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues'
PACKAGE_URL='https://www.msweet.org/pdfio' PACKAGE_URL='https://www.msweet.org/pdfio'
@ -1295,7 +1295,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing. # Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF cat <<_ACEOF
\`configure' configures pdfio 1.5.1 to adapt to many kinds of systems. \`configure' configures pdfio 1.5.2 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1361,7 +1361,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of pdfio 1.5.1:";; short | recursive ) echo "Configuration of pdfio 1.5.2:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@ -1460,7 +1460,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
pdfio configure 1.5.1 pdfio configure 1.5.2
generated by GNU Autoconf 2.71 generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc. Copyright (C) 2021 Free Software Foundation, Inc.
@ -1513,39 +1513,6 @@ fi
} # ac_fn_c_try_compile } # ac_fn_c_try_compile
# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
# -------------------------------------------------------
# Tests whether HEADER exists and can be compiled using the include files in
# INCLUDES, setting the cache variable VAR accordingly.
ac_fn_c_check_header_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
printf %s "checking for $2... " >&6; }
if eval test \${$3+y}
then :
printf %s "(cached) " >&6
else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
#include <$2>
_ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
eval "$3=yes"
else $as_nop
eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
eval ac_res=\$$3
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_compile
# ac_fn_c_try_link LINENO # ac_fn_c_try_link LINENO
# ----------------------- # -----------------------
# Try to link conftest.$ac_ext, and return whether this succeeded. # Try to link conftest.$ac_ext, and return whether this succeeded.
@ -1592,6 +1559,101 @@ fi
as_fn_set_status $ac_retval as_fn_set_status $ac_retval
} # ac_fn_c_try_link } # ac_fn_c_try_link
# ac_fn_c_check_func LINENO FUNC VAR
# ----------------------------------
# Tests whether FUNC exists, setting the cache variable VAR accordingly
ac_fn_c_check_func ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
printf %s "checking for $2... " >&6; }
if eval test \${$3+y}
then :
printf %s "(cached) " >&6
else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
For example, HP-UX 11i <limits.h> declares gettimeofday. */
#define $2 innocuous_$2
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char $2 (); below. */
#include <limits.h>
#undef $2
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char $2 ();
/* The GNU C library defines this for functions which it implements
to always fail with ENOSYS. Some functions are actually named
something starting with __ and the normal name is an alias. */
#if defined __stub_$2 || defined __stub___$2
choke me
#endif
int
main (void)
{
return $2 ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"
then :
eval "$3=yes"
else $as_nop
eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
eval ac_res=\$$3
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_func
# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
# -------------------------------------------------------
# Tests whether HEADER exists and can be compiled using the include files in
# INCLUDES, setting the cache variable VAR accordingly.
ac_fn_c_check_header_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
printf %s "checking for $2... " >&6; }
if eval test \${$3+y}
then :
printf %s "(cached) " >&6
else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
#include <$2>
_ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
eval "$3=yes"
else $as_nop
eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
eval ac_res=\$$3
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_compile
ac_configure_args_raw= ac_configure_args_raw=
for ac_arg for ac_arg
do do
@ -1616,7 +1678,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. running configure, to aid debugging if configure makes a mistake.
It was created by pdfio $as_me 1.5.1, which was It was created by pdfio $as_me 1.5.2, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw $ $0$ac_configure_args_raw
@ -2372,9 +2434,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
PDFIO_VERSION="1.5.1" PDFIO_VERSION="1.5.2"
PDFIO_VERSION_MAJOR="`echo 1.5.1 | awk -F. '{print $1}'`" PDFIO_VERSION_MAJOR="`echo 1.5.2 | awk -F. '{print $1}'`"
PDFIO_VERSION_MINOR="`echo 1.5.1 | awk -F. '{printf("%d\n",$2);}'`" PDFIO_VERSION_MINOR="`echo 1.5.2 | awk -F. '{printf("%d\n",$2);}'`"
@ -3877,6 +3939,56 @@ INSTALL="$(pwd)/install-sh"
printf "%s\n" "using $INSTALL" >&6; } printf "%s\n" "using $INSTALL" >&6; }
ac_fn_c_check_func "$LINENO" "timegm" "ac_cv_func_timegm"
if test "x$ac_cv_func_timegm" = xyes
then :
printf "%s\n" "#define HAVE_TIMEGM 1" >>confdefs.h
CPPFLAGS="-DHAVE_TIMEGM=1 $CPPFLAGS"
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for tm_gmtoff member in tm structure" >&5
printf %s "checking for tm_gmtoff member in tm structure... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <time.h>
int
main (void)
{
struct tm t;
int o = t.tm_gmtoff;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
printf "%s\n" "#define HAVE_TM_GMTOFF 1" >>confdefs.h
CPPFLAGS="-DHAVE_TM_GMTOFF=1 $CPPFLAGS"
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test -n "$ac_tool_prefix"; then if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
@ -3998,7 +4110,6 @@ PKGCONFIG_REQUIRES="zlib"
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib via pkg-config" >&5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib via pkg-config" >&5
printf %s "checking for zlib via pkg-config... " >&6; } printf %s "checking for zlib via pkg-config... " >&6; }
ac_header= ac_cache= ac_header= ac_cache=
for ac_item in $ac_header_c_list for ac_item in $ac_header_c_list
do do
@ -4988,7 +5099,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by pdfio $as_me 1.5.1, which was This file was extended by pdfio $as_me 1.5.2, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@ -5044,7 +5155,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped' ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\ ac_cs_version="\\
pdfio config.status 1.5.1 pdfio config.status 1.5.2
configured by $0, generated by GNU Autoconf 2.71, configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"

View File

@ -21,7 +21,7 @@ AC_PREREQ([2.70])
dnl Package name and version... dnl Package name and version...
AC_INIT([pdfio], [1.5.1], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio]) AC_INIT([pdfio], [1.5.2], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio])
PDFIO_VERSION="AC_PACKAGE_VERSION" PDFIO_VERSION="AC_PACKAGE_VERSION"
PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`" PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`"
@ -88,6 +88,27 @@ AC_SUBST([INSTALL])
AC_MSG_RESULT([using $INSTALL]) AC_MSG_RESULT([using $INSTALL])
dnl Check for date/time functionality...
AC_CHECK_FUNC([timegm], [
AC_DEFINE([HAVE_TIMEGM], [1], [Do we have the timegm function?])
CPPFLAGS="-DHAVE_TIMEGM=1 $CPPFLAGS"
])
AC_MSG_CHECKING([for tm_gmtoff member in tm structure])
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM([[#include <time.h>]], [[
struct tm t;
int o = t.tm_gmtoff;
]])
], [
AC_MSG_RESULT([yes])
AC_DEFINE([HAVE_TM_GMTOFF], [1], [Have tm_gmtoff member in struct tm?])
CPPFLAGS="-DHAVE_TM_GMTOFF=1 $CPPFLAGS"
], [
AC_MSG_RESULT([no])
])
dnl Check for pkg-config, which is used for some other tests later on... dnl Check for pkg-config, which is used for some other tests later on...
AC_PATH_TOOL([PKGCONFIG], [pkg-config]) AC_PATH_TOOL([PKGCONFIG], [pkg-config])

View File

@ -351,7 +351,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
if ((v->value.dict = _pdfioDictRead(pdf, obj, tb, depth + 1)) == NULL) if ((v->value.dict = _pdfioDictRead(pdf, obj, tb, depth + 1)) == NULL)
return (NULL); return (NULL);
} }
else if (!strncmp(token, "(D:", 3) && (timeval = get_date_time(token + 1)) != 0) else if ((timeval = get_date_time(token + 1)) != 0)
{ {
v->type = PDFIO_VALTYPE_DATE; v->type = PDFIO_VALTYPE_DATE;
v->value.date = timeval; v->value.date = timeval;
@ -707,31 +707,59 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
// 'get_date_time()' - Convert PDF date/time value to time_t. // 'get_date_time()' - Convert PDF date/time value to time_t.
// //
static time_t // O - Time in seconds static time_t // O - Time in seconds or `0` for none
get_date_time(const char *s) // I - PDF date/time value get_date_time(const char *s) // I - PDF date/time value
{ {
int i; // Looping var int i; // Looping var
struct tm dateval; // Date value struct tm dateval; // Date value
int offset; // Date offset int offset = 0; // Date offset in seconds
time_t t; // Time value
PDFIO_DEBUG("get_date_time(s=\"%s\")\n", s); PDFIO_DEBUG("get_date_time(s=\"%s\")\n", s);
// Possible date value of the form: // Possible date value of the form:
// //
// (D:YYYYMMDDhhmmssZ) // D:YYYYMMDDhhmmssZ
// (D:YYYYMMDDhhmmss+HH'mm) // D:YYYYMMDDhhmmss+HH'mm
// (D:YYYYMMDDhhmmss-HH'mm) // D:YYYYMMDDhhmmss-HH'mm
// //
if (strncmp(s, "D:", 2))
return (0);
for (i = 2; i < 16; i ++) for (i = 2; i < 16; i ++)
{ {
// Look for date/time digits...
if (!isdigit(s[i] & 255) || !s[i]) if (!isdigit(s[i] & 255) || !s[i])
break; break;
} }
if (i >= 16) if (i < 6 || (i & 1))
{ {
// Short year or missing digit...
return (0);
}
memset(&dateval, 0, sizeof(dateval));
dateval.tm_year = (s[2] - '0') * 1000 + (s[3] - '0') * 100 + (s[4] - '0') * 10 + s[5] - '0' - 1900;
if (i > 6)
dateval.tm_mon = (s[6] - '0') * 10 + s[7] - '0' - 1;
if (i > 8)
dateval.tm_mday = (s[8] - '0') * 10 + s[9] - '0';
else
dateval.tm_mday = 1;
if (i > 10)
dateval.tm_hour = (s[10] - '0') * 10 + s[11] - '0';
if (i > 12)
dateval.tm_min = (s[12] - '0') * 10 + s[13] - '0';
if (i > 14)
dateval.tm_sec = (s[14] - '0') * 10 + s[15] - '0';
if (i >= 16 && s[i])
{
// Get zone info...
if (s[i] == 'Z') if (s[i] == 'Z')
{ {
// UTC... // UTC...
@ -742,14 +770,20 @@ get_date_time(const char *s) // I - PDF date/time value
// Timezone offset from UTC... // Timezone offset from UTC...
if (isdigit(s[i + 1] & 255) && isdigit(s[i + 2] & 255) && s[i + 3] == '\'' && isdigit(s[i + 4] & 255) && isdigit(s[i + 5] & 255)) if (isdigit(s[i + 1] & 255) && isdigit(s[i + 2] & 255) && s[i + 3] == '\'' && isdigit(s[i + 4] & 255) && isdigit(s[i + 5] & 255))
{ {
offset = (s[i + 1] - '0') * 36000 + (s[i + 2] - '0') * 3600 + (s[i + 4] - '0') * 600 + (s[i + 5] - '0') * 60;
if (s[i] == '-')
offset = -offset;
i += 6; i += 6;
// Accept trailing quote, per PDF spec...
if (s[i] == '\'') if (s[i] == '\'')
i ++; i ++;
} }
} }
else if (!s[i]) else
{ {
// Missing zone info, invalid date string... // Random zone info, invalid date string...
return (0); return (0);
} }
} }
@ -760,26 +794,24 @@ get_date_time(const char *s) // I - PDF date/time value
return (0); return (0);
} }
// Date value... // Convert date value to time_t...
memset(&dateval, 0, sizeof(dateval)); #if _WIN32
if ((t = _mkgmtime(&dateval)) < 0)
return (0);
#elif defined(HAVE_TIMEGM)
if ((t = timegm(&dateval)) < 0)
return (0);
#else
if ((t = mktime(&dateval)) < 0)
return (0);
dateval.tm_year = (s[2] - '0') * 1000 + (s[3] - '0') * 100 + (s[4] - '0') * 10 + s[5] - '0' - 1900; # if defined(HAVE_TM_GMTOFF)
dateval.tm_mon = (s[6] - '0') * 10 + s[7] - '0' - 1; localtime_r(&t, &dateval);
dateval.tm_mday = (s[8] - '0') * 10 + s[9] - '0'; t -= dateval.tm_gmtoff;
dateval.tm_hour = (s[10] - '0') * 10 + s[11] - '0'; # else
dateval.tm_min = (s[12] - '0') * 10 + s[13] - '0'; t -= timezone;
dateval.tm_sec = (s[14] - '0') * 10 + s[15] - '0'; # endif // HAVE_TM_GMTOFF
#endif // _WIN32
if (s[16] == 'Z') return (t + offset);
{
offset = 0;
}
else
{
offset = (s[17] - '0') * 600 + (s[18] - '0') * 60 + (s[19] - '0') * 10 + s[20] - '0';
if (s[16] == '-')
offset = -offset;
}
return (mktime(&dateval) + offset);
} }