2021-04-10 14:00:52 +02:00
|
|
|
|
//
|
2021-05-30 13:10:44 +02:00
|
|
|
|
// PDF value functions for PDFio.
|
2021-04-10 14:00:52 +02:00
|
|
|
|
//
|
2023-02-04 02:39:04 +01:00
|
|
|
|
// Copyright © 2021-2023 by Michael R Sweet.
|
2021-04-10 14:00:52 +02:00
|
|
|
|
//
|
|
|
|
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
|
|
|
|
// information.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#include "pdfio-private.h"
|
2021-04-17 03:09:43 +02:00
|
|
|
|
|
|
|
|
|
|
2023-12-14 22:02:26 +01:00
|
|
|
|
//
|
|
|
|
|
// Local functions...
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
static time_t get_date_time(const char *s);
|
|
|
|
|
|
|
|
|
|
|
2021-04-17 03:09:43 +02:00
|
|
|
|
//
|
2021-04-28 03:22:34 +02:00
|
|
|
|
// '_pdfioValueCopy()' - Copy a value to a PDF file.
|
2021-04-17 03:09:43 +02:00
|
|
|
|
//
|
|
|
|
|
|
2021-04-28 03:22:34 +02:00
|
|
|
|
_pdfio_value_t *
|
|
|
|
|
_pdfioValueCopy(pdfio_file_t *pdfdst, // I - Destination PDF file
|
|
|
|
|
_pdfio_value_t *vdst, // I - Destination value
|
|
|
|
|
pdfio_file_t *pdfsrc, // I - Source PDF file
|
|
|
|
|
_pdfio_value_t *vsrc) // I - Source value
|
2021-04-17 03:09:43 +02:00
|
|
|
|
{
|
2021-05-15 15:29:37 +02:00
|
|
|
|
pdfio_obj_t *obj; // Object reference
|
2021-05-30 02:00:48 +02:00
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
static const char * const types[] = // Type strings for debug
|
|
|
|
|
{
|
|
|
|
|
"PDFIO_VALTYPE_NONE",
|
|
|
|
|
"PDFIO_VALTYPE_ARRAY",
|
|
|
|
|
"PDFIO_VALTYPE_BINARY",
|
|
|
|
|
"PDFIO_VALTYPE_BOOLEAN",
|
|
|
|
|
"PDFIO_VALTYPE_DATE",
|
|
|
|
|
"PDFIO_VALTYPE_DICT",
|
|
|
|
|
"PDFIO_VALTYPE_INDIRECT",
|
|
|
|
|
"PDFIO_VALTYPE_NAME",
|
|
|
|
|
"PDFIO_VALTYPE_NULL",
|
|
|
|
|
"PDFIO_VALTYPE_NUMBER",
|
|
|
|
|
"PDFIO_VALTYPE_STRING"
|
|
|
|
|
};
|
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
2021-05-15 15:29:37 +02:00
|
|
|
|
|
2021-05-30 02:00:48 +02:00
|
|
|
|
PDFIO_DEBUG("_pdfioValueCopy(pdfdst=%p, vdst=%p, pdfsrc=%p, vsrc=%p(%s))\n", pdfdst, vdst, pdfsrc, vsrc, types[vsrc->type]);
|
2021-05-15 15:29:37 +02:00
|
|
|
|
|
2021-04-30 13:42:25 +02:00
|
|
|
|
if (pdfdst == pdfsrc && vsrc->type != PDFIO_VALTYPE_BINARY)
|
|
|
|
|
{
|
|
|
|
|
// For the same document we can copy the values without any other effort
|
|
|
|
|
// unless there is a binary (hex string) value...
|
|
|
|
|
*vdst = *vsrc;
|
|
|
|
|
return (vdst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Not the same document or a binary value, do a deep copy...
|
|
|
|
|
switch (vsrc->type)
|
|
|
|
|
{
|
|
|
|
|
case PDFIO_VALTYPE_INDIRECT :
|
2021-06-07 14:34:30 +02:00
|
|
|
|
if ((obj = _pdfioFileFindMappedObj(pdfdst, pdfsrc, vsrc->value.indirect.number)) == NULL)
|
2021-05-15 15:29:37 +02:00
|
|
|
|
{
|
2021-06-07 14:34:30 +02:00
|
|
|
|
obj = pdfioObjCopy(pdfdst, pdfioFileFindObj(pdfsrc, vsrc->value.indirect.number));
|
2021-05-15 15:29:37 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!obj)
|
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
|
|
vdst->value.indirect.number = obj->number;
|
|
|
|
|
vdst->value.indirect.generation = obj->generation;
|
|
|
|
|
break;
|
2021-04-30 13:42:25 +02:00
|
|
|
|
|
|
|
|
|
default :
|
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_ARRAY :
|
|
|
|
|
vdst->value.array = pdfioArrayCopy(pdfdst, vsrc->value.array);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_BINARY :
|
|
|
|
|
if ((vdst->value.binary.data = (unsigned char *)malloc(vsrc->value.binary.datalen)) == NULL)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(pdfdst, "Unable to allocate memory for a binary string - %s", strerror(errno));
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
2021-04-28 03:22:34 +02:00
|
|
|
|
|
2021-04-30 13:42:25 +02:00
|
|
|
|
vdst->value.binary.datalen = vsrc->value.binary.datalen;
|
|
|
|
|
memcpy(vdst->value.binary.data, vsrc->value.binary.data, vdst->value.binary.datalen);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_BOOLEAN :
|
|
|
|
|
case PDFIO_VALTYPE_DATE :
|
|
|
|
|
case PDFIO_VALTYPE_NUMBER :
|
|
|
|
|
*vdst = *vsrc;
|
|
|
|
|
return (vdst);
|
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_DICT :
|
|
|
|
|
vdst->value.dict = pdfioDictCopy(pdfdst, vsrc->value.dict);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_NAME :
|
|
|
|
|
case PDFIO_VALTYPE_STRING :
|
|
|
|
|
vdst->value.name = pdfioStringCreate(pdfdst, vsrc->value.name);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vdst->type = vsrc->type;
|
|
|
|
|
|
|
|
|
|
return (vdst);
|
2021-04-28 03:22:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-12-14 22:02:26 +01:00
|
|
|
|
//
|
|
|
|
|
// '_pdfioValueDecrypt()' - Decrypt a value.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
bool // O - `true` on success, `false` on error
|
|
|
|
|
_pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
|
|
|
|
|
pdfio_obj_t *obj, // I - Object
|
|
|
|
|
_pdfio_value_t *v, // I - Value
|
|
|
|
|
size_t depth)// I - Depth
|
|
|
|
|
{
|
|
|
|
|
_pdfio_crypto_ctx_t ctx; // Decryption context
|
|
|
|
|
_pdfio_crypto_cb_t cb; // Decryption callback
|
|
|
|
|
size_t ivlen; // Number of initialization vector bytes
|
|
|
|
|
uint8_t temp[32768]; // Temporary buffer for decryption
|
|
|
|
|
size_t templen; // Number of actual data bytes
|
|
|
|
|
time_t timeval; // Date/time value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (depth > PDFIO_MAX_DEPTH)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(pdf, "Value too deep.");
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (v->type)
|
|
|
|
|
{
|
2024-01-24 16:58:11 +01:00
|
|
|
|
default :
|
|
|
|
|
// Do nothing
|
|
|
|
|
break;
|
|
|
|
|
|
2023-12-14 22:02:26 +01:00
|
|
|
|
case PDFIO_VALTYPE_ARRAY :
|
|
|
|
|
return (_pdfioArrayDecrypt(pdf, obj, v->value.array, depth + 1));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_DICT :
|
|
|
|
|
return (_pdfioDictDecrypt(pdf, obj, v->value.dict, depth + 1));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_BINARY :
|
|
|
|
|
// Decrypt the binary string...
|
|
|
|
|
if (v->value.binary.datalen > (sizeof(temp) - 32))
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(pdf, "Unable to read encrypted binary string - too long.");
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ivlen = v->value.binary.datalen;
|
|
|
|
|
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, v->value.binary.data, &ivlen)) == NULL)
|
|
|
|
|
return (false);
|
|
|
|
|
|
|
|
|
|
templen = (cb)(&ctx, temp, v->value.binary.data + ivlen, v->value.binary.datalen - ivlen);
|
|
|
|
|
|
|
|
|
|
// Copy the decrypted string back to the value and adjust the length...
|
|
|
|
|
memcpy(v->value.binary.data, temp, templen);
|
|
|
|
|
|
|
|
|
|
if (pdf->encryption >= PDFIO_ENCRYPTION_AES_128)
|
|
|
|
|
v->value.binary.datalen = templen - temp[templen - 1];
|
|
|
|
|
else
|
|
|
|
|
v->value.binary.datalen = templen;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_STRING :
|
|
|
|
|
// Decrypt regular string...
|
|
|
|
|
templen = strlen(v->value.string);
|
|
|
|
|
if (templen > (sizeof(temp) - 33))
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(pdf, "Unable to read encrypted string - too long.");
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ivlen = templen;
|
|
|
|
|
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, (uint8_t *)v->value.string, &ivlen)) == NULL)
|
|
|
|
|
return (false);
|
|
|
|
|
|
|
|
|
|
templen = (cb)(&ctx, temp, (uint8_t *)v->value.string + ivlen, templen - ivlen);
|
|
|
|
|
temp[templen] = '\0';
|
|
|
|
|
|
|
|
|
|
if ((timeval = get_date_time((char *)temp)) != 0)
|
|
|
|
|
{
|
|
|
|
|
// Change the type to date...
|
|
|
|
|
v->type = PDFIO_VALTYPE_DATE;
|
|
|
|
|
v->value.date = timeval;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Copy the decrypted string back to the value...
|
|
|
|
|
v->value.string = pdfioStringCreate(pdf, (char *)temp);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-05-06 15:51:48 +02:00
|
|
|
|
//
|
|
|
|
|
// '_pdfioValueDebug()' - Print the contents of a value.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
void
|
2021-05-10 14:40:52 +02:00
|
|
|
|
_pdfioValueDebug(_pdfio_value_t *v, // I - Value
|
|
|
|
|
FILE *fp) // I - Output file
|
2021-05-06 15:51:48 +02:00
|
|
|
|
{
|
|
|
|
|
switch (v->type)
|
|
|
|
|
{
|
|
|
|
|
case PDFIO_VALTYPE_ARRAY :
|
2021-05-10 14:40:52 +02:00
|
|
|
|
_pdfioArrayDebug(v->value.array, fp);
|
2021-05-06 15:51:48 +02:00
|
|
|
|
break;
|
|
|
|
|
case PDFIO_VALTYPE_BINARY :
|
|
|
|
|
{
|
|
|
|
|
size_t i; // Looping var
|
|
|
|
|
unsigned char *ptr; // Pointer into data
|
|
|
|
|
|
2021-05-10 14:40:52 +02:00
|
|
|
|
putc('<', fp);
|
2021-05-06 15:51:48 +02:00
|
|
|
|
for (i = v->value.binary.datalen, ptr = v->value.binary.data; i > 0; i --, ptr ++)
|
2021-05-10 14:40:52 +02:00
|
|
|
|
fprintf(fp, "%02X", *ptr);
|
|
|
|
|
putc('>', fp);
|
2021-05-06 15:51:48 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case PDFIO_VALTYPE_BOOLEAN :
|
2021-05-10 14:40:52 +02:00
|
|
|
|
fputs(v->value.boolean ? " true" : " false", fp);
|
2021-05-06 15:51:48 +02:00
|
|
|
|
break;
|
|
|
|
|
case PDFIO_VALTYPE_DATE :
|
2021-07-08 04:06:25 +02:00
|
|
|
|
{
|
|
|
|
|
struct tm dateval; // Date value
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
gmtime_s(&dateval, &v->value.date);
|
|
|
|
|
#else
|
|
|
|
|
gmtime_r(&v->value.date, &dateval);
|
|
|
|
|
#endif // _WIN32
|
|
|
|
|
|
|
|
|
|
fprintf(fp, "(D:%04d%02d%02d%02d%02d%02dZ)", dateval.tm_year + 1900, dateval.tm_mon + 1, dateval.tm_mday, dateval.tm_hour, dateval.tm_min, dateval.tm_sec);
|
|
|
|
|
}
|
2021-05-06 15:51:48 +02:00
|
|
|
|
break;
|
|
|
|
|
case PDFIO_VALTYPE_DICT :
|
2021-05-10 14:40:52 +02:00
|
|
|
|
fputs("<<", fp);
|
|
|
|
|
_pdfioDictDebug(v->value.dict, fp);
|
|
|
|
|
fputs(">>", fp);
|
2021-05-06 15:51:48 +02:00
|
|
|
|
break;
|
|
|
|
|
case PDFIO_VALTYPE_INDIRECT :
|
2021-05-10 14:40:52 +02:00
|
|
|
|
fprintf(fp, " %lu %u R", (unsigned long)v->value.indirect.number, v->value.indirect.generation);
|
2021-05-06 15:51:48 +02:00
|
|
|
|
break;
|
|
|
|
|
case PDFIO_VALTYPE_NAME :
|
2021-05-10 14:40:52 +02:00
|
|
|
|
fprintf(fp, "/%s", v->value.name);
|
2021-05-06 15:51:48 +02:00
|
|
|
|
break;
|
|
|
|
|
case PDFIO_VALTYPE_NULL :
|
2021-05-10 14:40:52 +02:00
|
|
|
|
fputs(" null", fp);
|
2021-05-06 15:51:48 +02:00
|
|
|
|
break;
|
|
|
|
|
case PDFIO_VALTYPE_NUMBER :
|
2021-05-10 14:40:52 +02:00
|
|
|
|
fprintf(fp, " %g", v->value.number);
|
2021-05-06 15:51:48 +02:00
|
|
|
|
break;
|
|
|
|
|
case PDFIO_VALTYPE_STRING :
|
2021-05-10 14:40:52 +02:00
|
|
|
|
fprintf(fp, "(%s)", v->value.string);
|
2021-05-06 15:51:48 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default :
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-04-28 03:22:34 +02:00
|
|
|
|
//
|
|
|
|
|
// '_pdfioValueDelete()' - Free the memory used by a value.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
_pdfioValueDelete(_pdfio_value_t *v) // I - Value
|
|
|
|
|
{
|
2021-04-30 13:42:25 +02:00
|
|
|
|
if (v->type == PDFIO_VALTYPE_BINARY)
|
|
|
|
|
free(v->value.binary.data);
|
2021-04-17 03:09:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-05-04 16:34:17 +02:00
|
|
|
|
//
|
|
|
|
|
// '_pdfioValueRead()' - Read a value from a file.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
_pdfio_value_t * // O - Value or `NULL` on error/EOF
|
|
|
|
|
_pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
2021-10-24 00:08:16 +02:00
|
|
|
|
pdfio_obj_t *obj, // I - Object, if any
|
2021-05-08 13:38:44 +02:00
|
|
|
|
_pdfio_token_t *tb, // I - Token buffer/stack
|
2021-11-29 23:46:56 +01:00
|
|
|
|
_pdfio_value_t *v, // I - Value
|
|
|
|
|
size_t depth) // I - Depth of value
|
2021-05-04 16:34:17 +02:00
|
|
|
|
{
|
2021-08-24 01:37:33 +02:00
|
|
|
|
char token[32768]; // Token buffer
|
2023-12-14 22:02:26 +01:00
|
|
|
|
time_t timeval; // Date/time value
|
2021-05-04 18:59:10 +02:00
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
static const char * const valtypes[] =
|
|
|
|
|
{
|
|
|
|
|
"<<none>>", // No value, not set
|
|
|
|
|
"array", // Array
|
|
|
|
|
"hex-string", // Binary data
|
|
|
|
|
"boolean", // Boolean
|
|
|
|
|
"date", // Date/time
|
|
|
|
|
"dict", // Dictionary
|
|
|
|
|
"indirect", // Indirect object (N G obj)
|
|
|
|
|
"name", // Name
|
|
|
|
|
"null", // Null object
|
|
|
|
|
"number", // Number (integer or real)
|
|
|
|
|
"string" // String
|
|
|
|
|
};
|
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
|
|
|
|
|
2021-10-24 00:08:16 +02:00
|
|
|
|
PDFIO_DEBUG("_pdfioValueRead(pdf=%p, obj=%p, v=%p)\n", pdf, obj, v);
|
2021-05-04 16:34:17 +02:00
|
|
|
|
|
2021-05-08 13:38:44 +02:00
|
|
|
|
if (!_pdfioTokenGet(tb, token, sizeof(token)))
|
2021-05-04 16:34:17 +02:00
|
|
|
|
return (NULL);
|
|
|
|
|
|
2021-05-04 18:59:10 +02:00
|
|
|
|
if (!strcmp(token, "["))
|
|
|
|
|
{
|
|
|
|
|
// Start of array
|
2021-11-29 23:46:56 +01:00
|
|
|
|
if (depth >= PDFIO_MAX_DEPTH)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(pdf, "Too many nested arrays.");
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-04 18:59:10 +02:00
|
|
|
|
v->type = PDFIO_VALTYPE_ARRAY;
|
2021-11-29 23:46:56 +01:00
|
|
|
|
if ((v->value.array = _pdfioArrayRead(pdf, obj, tb, depth + 1)) == NULL)
|
2021-05-04 18:59:10 +02:00
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(token, "<<"))
|
|
|
|
|
{
|
|
|
|
|
// Start of dictionary
|
2021-11-29 23:46:56 +01:00
|
|
|
|
if (depth >= PDFIO_MAX_DEPTH)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(pdf, "Too many nested dictionaries.");
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-04 18:59:10 +02:00
|
|
|
|
v->type = PDFIO_VALTYPE_DICT;
|
2021-11-29 23:46:56 +01:00
|
|
|
|
if ((v->value.dict = _pdfioDictRead(pdf, obj, tb, depth + 1)) == NULL)
|
2021-05-04 18:59:10 +02:00
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
2023-12-14 22:02:26 +01:00
|
|
|
|
else if (!strncmp(token, "(D:", 3) && (timeval = get_date_time(token + 1)) != 0)
|
2021-07-08 04:06:25 +02:00
|
|
|
|
{
|
2023-12-14 22:02:26 +01:00
|
|
|
|
v->type = PDFIO_VALTYPE_DATE;
|
|
|
|
|
v->value.date = timeval;
|
2021-07-08 04:06:25 +02:00
|
|
|
|
}
|
2021-05-04 18:59:10 +02:00
|
|
|
|
else if (token[0] == '(')
|
2021-05-04 16:34:17 +02:00
|
|
|
|
{
|
|
|
|
|
// String
|
|
|
|
|
v->type = PDFIO_VALTYPE_STRING;
|
|
|
|
|
v->value.string = pdfioStringCreate(pdf, token + 1);
|
|
|
|
|
}
|
2021-05-05 03:31:58 +02:00
|
|
|
|
else if (token[0] == '/')
|
|
|
|
|
{
|
|
|
|
|
// Name
|
|
|
|
|
v->type = PDFIO_VALTYPE_NAME;
|
|
|
|
|
v->value.name = pdfioStringCreate(pdf, token + 1);
|
|
|
|
|
}
|
2021-05-04 16:34:17 +02:00
|
|
|
|
else if (token[0] == '<')
|
|
|
|
|
{
|
|
|
|
|
// Hex string
|
|
|
|
|
const char *tokptr; // Pointer into token
|
|
|
|
|
unsigned char *dataptr; // Pointer into data
|
|
|
|
|
|
|
|
|
|
v->type = PDFIO_VALTYPE_BINARY;
|
|
|
|
|
v->value.binary.datalen = strlen(token) / 2;
|
|
|
|
|
if ((v->value.binary.data = (unsigned char *)malloc(v->value.binary.datalen)) == NULL)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(pdf, "Out of memory for hex string.");
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert hex to binary...
|
|
|
|
|
tokptr = token + 1;
|
|
|
|
|
dataptr = v->value.binary.data;
|
|
|
|
|
|
|
|
|
|
while (*tokptr)
|
|
|
|
|
{
|
|
|
|
|
int d; // Data value
|
|
|
|
|
|
|
|
|
|
if (isdigit(*tokptr))
|
|
|
|
|
d = (*tokptr++ - '0') << 4;
|
|
|
|
|
else
|
|
|
|
|
d = (tolower(*tokptr++) - 'a' + 10) << 4;
|
|
|
|
|
|
|
|
|
|
if (*tokptr)
|
|
|
|
|
{
|
|
|
|
|
// PDF allows writers to drop a trailing 0...
|
|
|
|
|
if (isdigit(*tokptr))
|
|
|
|
|
d |= *tokptr++ - '0';
|
|
|
|
|
else
|
|
|
|
|
d |= tolower(*tokptr++) - 'a' + 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*dataptr++ = (unsigned char)d;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (strchr("0123456789-+.", token[0]) != NULL)
|
|
|
|
|
{
|
|
|
|
|
// Number or indirect object reference
|
|
|
|
|
if (isdigit(token[0]) && !strchr(token, '.'))
|
|
|
|
|
{
|
|
|
|
|
// Integer or object ref...
|
2021-08-23 20:31:54 +02:00
|
|
|
|
unsigned char *tempptr; // Pointer into buffer
|
2021-05-04 16:34:17 +02:00
|
|
|
|
|
2021-08-24 19:49:43 +02:00
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
PDFIO_DEBUG("_pdfioValueRead: %d bytes left in buffer: '", (int)(tb->bufend - tb->bufptr));
|
|
|
|
|
for (tempptr = tb->bufptr; tempptr < tb->bufend; tempptr ++)
|
|
|
|
|
{
|
|
|
|
|
if (*tempptr < ' ' || *tempptr == 0x7f)
|
|
|
|
|
PDFIO_DEBUG("\\%03o", *tempptr);
|
|
|
|
|
else
|
|
|
|
|
PDFIO_DEBUG("%c", *tempptr);
|
|
|
|
|
}
|
|
|
|
|
PDFIO_DEBUG("'.\n");
|
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
2021-08-23 20:31:54 +02:00
|
|
|
|
if ((tb->bufend - tb->bufptr) < 10)
|
2021-05-04 16:34:17 +02:00
|
|
|
|
{
|
2021-08-23 20:31:54 +02:00
|
|
|
|
// Fill up buffer...
|
|
|
|
|
ssize_t bytes; // Bytes peeked
|
2021-05-04 16:34:17 +02:00
|
|
|
|
|
2021-08-23 20:31:54 +02:00
|
|
|
|
_pdfioTokenFlush(tb);
|
2021-05-04 16:34:17 +02:00
|
|
|
|
|
2021-08-23 20:31:54 +02:00
|
|
|
|
if ((bytes = (tb->peek_cb)(tb->cb_data, tb->buffer, sizeof(tb->buffer))) > 0)
|
|
|
|
|
tb->bufend = tb->buffer + bytes;
|
2021-05-05 03:31:58 +02:00
|
|
|
|
|
2021-08-23 20:31:54 +02:00
|
|
|
|
#ifdef DEBUG
|
2021-08-24 19:49:43 +02:00
|
|
|
|
PDFIO_DEBUG("_pdfioValueRead: %d bytes now in buffer: '", (int)(tb->bufend - tb->bufptr));
|
|
|
|
|
for (tempptr = tb->bufptr; tempptr < tb->bufend; tempptr ++)
|
|
|
|
|
{
|
|
|
|
|
if (*tempptr < ' ' || *tempptr == 0x7f)
|
|
|
|
|
PDFIO_DEBUG("\\%03o", *tempptr);
|
|
|
|
|
else
|
|
|
|
|
PDFIO_DEBUG("%c", *tempptr);
|
|
|
|
|
}
|
|
|
|
|
PDFIO_DEBUG("'.\n");
|
2021-08-23 20:31:54 +02:00
|
|
|
|
#endif // DEBUG
|
2021-08-24 19:49:43 +02:00
|
|
|
|
}
|
2021-08-23 20:31:54 +02:00
|
|
|
|
|
|
|
|
|
tempptr = tb->bufptr;
|
|
|
|
|
|
2021-11-02 14:12:43 +01:00
|
|
|
|
while (tempptr < tb->bufend && isspace(*tempptr & 255))
|
|
|
|
|
tempptr ++; // Skip whitespace as needed...
|
|
|
|
|
|
2021-08-23 20:31:54 +02:00
|
|
|
|
if (tempptr < tb->bufend && isdigit(*tempptr & 255))
|
|
|
|
|
{
|
|
|
|
|
// Integer...
|
|
|
|
|
long generation = 0; // Generation number
|
|
|
|
|
|
|
|
|
|
while (tempptr < tb->bufend && isdigit(*tempptr & 255))
|
|
|
|
|
{
|
|
|
|
|
generation = generation * 10 + *tempptr - '0';
|
|
|
|
|
tempptr ++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (tempptr < tb->bufend && isspace(*tempptr & 255))
|
2021-11-02 14:12:43 +01:00
|
|
|
|
tempptr ++; // Skip whitespace
|
2021-08-23 20:31:54 +02:00
|
|
|
|
|
|
|
|
|
if (tempptr < tb->bufend && *tempptr == 'R')
|
|
|
|
|
{
|
|
|
|
|
// Reference!
|
|
|
|
|
PDFIO_DEBUG("_pdfioValueRead: Consuming %d bytes.\n", (int)(tempptr - tb->bufptr + 1));
|
|
|
|
|
tb->bufptr = tempptr + 1;
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
PDFIO_DEBUG("_pdfioValueRead: Next bytes are '");
|
|
|
|
|
for (tempptr = tb->bufptr; tempptr < tb->bufend; tempptr ++)
|
2021-05-04 16:34:17 +02:00
|
|
|
|
{
|
2021-08-23 20:31:54 +02:00
|
|
|
|
if (*tempptr < ' ' || *tempptr == 0x7f)
|
|
|
|
|
PDFIO_DEBUG("\\%03o", *tempptr);
|
|
|
|
|
else
|
|
|
|
|
PDFIO_DEBUG("%c", *tempptr);
|
2021-05-04 16:34:17 +02:00
|
|
|
|
}
|
2021-08-23 20:31:54 +02:00
|
|
|
|
PDFIO_DEBUG("'.\n");
|
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
|
|
|
|
v->type = PDFIO_VALTYPE_INDIRECT;
|
|
|
|
|
v->value.indirect.number = (size_t)strtoimax(token, NULL, 10);
|
|
|
|
|
v->value.indirect.generation = (unsigned short)generation;
|
|
|
|
|
|
|
|
|
|
PDFIO_DEBUG("_pdfioValueRead: Returning indirect value %lu %u R.\n", (unsigned long)v->value.indirect.number, v->value.indirect.generation);
|
|
|
|
|
|
|
|
|
|
return (v);
|
2021-05-04 16:34:17 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we get here, we have a number...
|
|
|
|
|
v->type = PDFIO_VALTYPE_NUMBER;
|
2021-05-30 03:16:21 +02:00
|
|
|
|
v->value.number = (double)strtod(token, NULL);
|
2021-05-04 16:34:17 +02:00
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(token, "true") || !strcmp(token, "false"))
|
|
|
|
|
{
|
|
|
|
|
// Boolean value
|
|
|
|
|
v->type = PDFIO_VALTYPE_BOOLEAN;
|
|
|
|
|
v->value.boolean = !strcmp(token, "true");
|
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(token, "null"))
|
|
|
|
|
{
|
|
|
|
|
// null value
|
|
|
|
|
v->type = PDFIO_VALTYPE_NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(pdf, "Unexpected '%s' token seen.", token);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-04 18:59:10 +02:00
|
|
|
|
PDFIO_DEBUG("_pdfioValueRead: Returning %s value.\n", valtypes[v->type]);
|
|
|
|
|
|
2021-05-04 16:34:17 +02:00
|
|
|
|
return (v);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-04-28 03:22:34 +02:00
|
|
|
|
//
|
|
|
|
|
// '_pdfioValueWrite()' - Write a value to a PDF file.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
bool // O - `true` on success, `false` on failure
|
|
|
|
|
_pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
|
2021-10-24 00:08:16 +02:00
|
|
|
|
pdfio_obj_t *obj, // I - Object, if any
|
2021-05-16 17:39:05 +02:00
|
|
|
|
_pdfio_value_t *v, // I - Value
|
|
|
|
|
off_t *length)// O - Offset to /Length value, if any
|
2021-04-28 03:22:34 +02:00
|
|
|
|
{
|
|
|
|
|
switch (v->type)
|
|
|
|
|
{
|
|
|
|
|
default :
|
|
|
|
|
return (false);
|
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_ARRAY :
|
2021-10-24 00:08:16 +02:00
|
|
|
|
return (_pdfioArrayWrite(v->value.array, obj));
|
2021-04-28 03:22:34 +02:00
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_BINARY :
|
2021-04-30 13:42:25 +02:00
|
|
|
|
{
|
2021-10-24 00:08:16 +02:00
|
|
|
|
size_t databytes; // Bytes to write
|
|
|
|
|
uint8_t temp[32768], // Temporary buffer for encryption
|
|
|
|
|
*dataptr; // Pointer into data
|
|
|
|
|
|
|
|
|
|
if (obj && pdf->encryption)
|
|
|
|
|
{
|
|
|
|
|
// Write encrypted string...
|
|
|
|
|
_pdfio_crypto_ctx_t ctx; // Encryption context
|
|
|
|
|
_pdfio_crypto_cb_t cb; // Encryption callback
|
|
|
|
|
size_t ivlen; // Number of initialization vector bytes
|
|
|
|
|
|
|
|
|
|
if (v->value.binary.datalen > (sizeof(temp) - 32))
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(pdf, "Unable to write encrypted binary string - too long.");
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen);
|
|
|
|
|
databytes = (cb)(&ctx, temp + ivlen, v->value.binary.data, v->value.binary.datalen) + ivlen;
|
|
|
|
|
dataptr = temp;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dataptr = v->value.binary.data;
|
|
|
|
|
databytes = v->value.binary.datalen;
|
|
|
|
|
}
|
2021-04-30 13:42:25 +02:00
|
|
|
|
|
|
|
|
|
if (!_pdfioFilePuts(pdf, "<"))
|
|
|
|
|
return (false);
|
|
|
|
|
|
2021-10-24 00:08:16 +02:00
|
|
|
|
for (; databytes > 1; databytes -= 2, dataptr += 2)
|
2021-04-30 13:42:25 +02:00
|
|
|
|
{
|
|
|
|
|
if (!_pdfioFilePrintf(pdf, "%02X%02X", dataptr[0], dataptr[1]))
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-24 00:08:16 +02:00
|
|
|
|
if (databytes > 0)
|
2021-04-30 13:42:25 +02:00
|
|
|
|
return (_pdfioFilePrintf(pdf, "%02X>", dataptr[0]));
|
|
|
|
|
else
|
|
|
|
|
return (_pdfioFilePuts(pdf, ">"));
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 03:22:34 +02:00
|
|
|
|
case PDFIO_VALTYPE_BOOLEAN :
|
|
|
|
|
if (v->value.boolean)
|
|
|
|
|
return (_pdfioFilePuts(pdf, " true"));
|
|
|
|
|
else
|
|
|
|
|
return (_pdfioFilePuts(pdf, " false"));
|
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_DATE :
|
2021-04-30 13:42:25 +02:00
|
|
|
|
{
|
|
|
|
|
struct tm date; // Date values
|
2021-10-24 00:08:16 +02:00
|
|
|
|
char datestr[32]; // Formatted date value
|
2021-04-30 13:42:25 +02:00
|
|
|
|
|
2021-06-21 17:39:06 +02:00
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
gmtime_s(&date, &v->value.date);
|
|
|
|
|
#else
|
|
|
|
|
gmtime_r(&v->value.date, &date);
|
|
|
|
|
#endif // _WIN32
|
2021-07-08 04:06:25 +02:00
|
|
|
|
|
2021-10-24 00:08:16 +02:00
|
|
|
|
snprintf(datestr, sizeof(datestr), "D:%04d%02d%02d%02d%02d%02dZ", date.tm_year + 1900, date.tm_mon + 1, date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec);
|
|
|
|
|
|
|
|
|
|
if (obj && pdf->encryption)
|
|
|
|
|
{
|
|
|
|
|
// Write encrypted string...
|
|
|
|
|
uint8_t temp[32768], // Encrypted bytes
|
|
|
|
|
*tempptr; // Pointer into encrypted bytes
|
|
|
|
|
_pdfio_crypto_ctx_t ctx; // Encryption context
|
|
|
|
|
_pdfio_crypto_cb_t cb; // Encryption callback
|
|
|
|
|
size_t len = strlen(datestr),
|
|
|
|
|
// Length of value
|
|
|
|
|
ivlen, // Number of initialization vector bytes
|
|
|
|
|
tempbytes; // Number of output bytes
|
|
|
|
|
|
|
|
|
|
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen);
|
|
|
|
|
tempbytes = (cb)(&ctx, temp + ivlen, (const uint8_t *)datestr, len) + ivlen;
|
|
|
|
|
|
|
|
|
|
if (!_pdfioFilePuts(pdf, "<"))
|
|
|
|
|
return (false);
|
|
|
|
|
|
|
|
|
|
for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2)
|
|
|
|
|
{
|
|
|
|
|
if (!_pdfioFilePrintf(pdf, "%02X%02X", tempptr[0], tempptr[1]))
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tempbytes > 0)
|
|
|
|
|
return (_pdfioFilePrintf(pdf, "%02X>", *tempptr));
|
|
|
|
|
else
|
|
|
|
|
return (_pdfioFilePuts(pdf, ">"));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return (_pdfioFilePrintf(pdf, "(%s)", datestr));
|
|
|
|
|
}
|
2021-04-30 13:42:25 +02:00
|
|
|
|
}
|
2021-04-28 03:22:34 +02:00
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_DICT :
|
2021-10-24 00:08:16 +02:00
|
|
|
|
return (_pdfioDictWrite(v->value.dict, obj, length));
|
2021-04-28 03:22:34 +02:00
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_INDIRECT :
|
2021-05-04 16:34:17 +02:00
|
|
|
|
return (_pdfioFilePrintf(pdf, " %lu %u R", (unsigned long)v->value.indirect.number, v->value.indirect.generation));
|
2021-04-28 03:22:34 +02:00
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_NAME :
|
|
|
|
|
return (_pdfioFilePrintf(pdf, "/%s", v->value.name));
|
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_NULL :
|
|
|
|
|
return (_pdfioFilePuts(pdf, " null"));
|
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_NUMBER :
|
|
|
|
|
return (_pdfioFilePrintf(pdf, " %g", v->value.number));
|
|
|
|
|
|
|
|
|
|
case PDFIO_VALTYPE_STRING :
|
2021-10-24 00:08:16 +02:00
|
|
|
|
if (obj && pdf->encryption)
|
|
|
|
|
{
|
|
|
|
|
// Write encrypted string...
|
|
|
|
|
uint8_t temp[32768], // Encrypted bytes
|
|
|
|
|
*tempptr; // Pointer into encrypted bytes
|
|
|
|
|
_pdfio_crypto_ctx_t ctx; // Encryption context
|
|
|
|
|
_pdfio_crypto_cb_t cb; // Encryption callback
|
|
|
|
|
size_t len = strlen(v->value.string),
|
|
|
|
|
// Length of value
|
|
|
|
|
ivlen, // Number of initialization vector bytes
|
|
|
|
|
tempbytes; // Number of output bytes
|
|
|
|
|
|
|
|
|
|
if (len > (sizeof(temp) - 32))
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(pdf, "Unable to write encrypted string - too long.");
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen);
|
|
|
|
|
tempbytes = (cb)(&ctx, temp + ivlen, (const uint8_t *)v->value.string, len) + ivlen;
|
|
|
|
|
|
|
|
|
|
if (!_pdfioFilePuts(pdf, "<"))
|
|
|
|
|
return (false);
|
|
|
|
|
|
|
|
|
|
for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2)
|
|
|
|
|
{
|
|
|
|
|
if (!_pdfioFilePrintf(pdf, "%02X%02X", tempptr[0], tempptr[1]))
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tempbytes > 0)
|
|
|
|
|
return (_pdfioFilePrintf(pdf, "%02X>", *tempptr));
|
|
|
|
|
else
|
|
|
|
|
return (_pdfioFilePuts(pdf, ">"));
|
|
|
|
|
}
|
|
|
|
|
else
|
2021-04-28 03:22:34 +02:00
|
|
|
|
{
|
2021-10-24 00:08:16 +02:00
|
|
|
|
// Write unencrypted string...
|
2021-04-28 03:22:34 +02:00
|
|
|
|
const char *start, // Start of fragment
|
|
|
|
|
*end; // End of fragment
|
|
|
|
|
|
|
|
|
|
if (!_pdfioFilePuts(pdf, "("))
|
|
|
|
|
return (false);
|
|
|
|
|
|
|
|
|
|
// Write a quoted string value...
|
|
|
|
|
for (start = v->value.string; *start; start = end)
|
|
|
|
|
{
|
|
|
|
|
// Find the next character that needs to be quoted...
|
|
|
|
|
for (end = start; *end; end ++)
|
|
|
|
|
{
|
|
|
|
|
if (*end == '\\' || *end == ')' || (*end & 255) < ' ')
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (end > start)
|
|
|
|
|
{
|
|
|
|
|
// Write unquoted (safe) characters...
|
|
|
|
|
if (!_pdfioFileWrite(pdf, start, (size_t)(end - start)))
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*end)
|
|
|
|
|
{
|
|
|
|
|
// Quote this character...
|
|
|
|
|
bool success; // Did the write work?
|
|
|
|
|
|
|
|
|
|
if (*end == '\\' || *end == ')')
|
|
|
|
|
success = _pdfioFilePrintf(pdf, "\\%c", *end);
|
|
|
|
|
else
|
|
|
|
|
success = _pdfioFilePrintf(pdf, "\\%03o", *end);
|
|
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
|
return (false);
|
|
|
|
|
|
|
|
|
|
end ++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (_pdfioFilePuts(pdf, ")"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
2023-12-14 22:02:26 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// 'get_date_time()' - Convert PDF date/time value to time_t.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
static time_t // O - Time in seconds
|
|
|
|
|
get_date_time(const char *s) // I - PDF date/time value
|
|
|
|
|
{
|
|
|
|
|
int i; // Looping var
|
|
|
|
|
struct tm dateval; // Date value
|
|
|
|
|
int offset; // Date offset
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Possible date value of the form:
|
|
|
|
|
//
|
|
|
|
|
// (D:YYYYMMDDhhmmssZ)
|
|
|
|
|
// (D:YYYYMMDDhhmmss+HH'mm)
|
|
|
|
|
// (D:YYYYMMDDhhmmss-HH'mm)
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
for (i = 2; i < 16; i ++)
|
|
|
|
|
{
|
|
|
|
|
if (!isdigit(s[i] & 255) || !s[i])
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i >= 16)
|
|
|
|
|
{
|
|
|
|
|
if (s[i] == 'Z')
|
|
|
|
|
{
|
|
|
|
|
i ++;
|
|
|
|
|
}
|
|
|
|
|
else if (s[i] == '-' || s[i] == '+')
|
|
|
|
|
{
|
|
|
|
|
if (isdigit(s[i + 1] & 255) && isdigit(s[i + 2] & 255) && s[i + 3] == '\'' && isdigit(s[i + 4] & 255) && isdigit(s[i + 5] & 255))
|
|
|
|
|
{
|
|
|
|
|
i += 6;
|
|
|
|
|
if (s[i] == '\'')
|
|
|
|
|
i ++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (s[i])
|
|
|
|
|
{
|
|
|
|
|
// Just a string...
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Date value...
|
|
|
|
|
memset(&dateval, 0, sizeof(dateval));
|
|
|
|
|
|
|
|
|
|
dateval.tm_year = (s[2] - '0') * 1000 + (s[3] - '0') * 100 + (s[4] - '0') * 10 + s[5] - '0' - 1900;
|
|
|
|
|
dateval.tm_mon = (s[6] - '0') * 10 + s[7] - '0' - 1;
|
|
|
|
|
dateval.tm_mday = (s[8] - '0') * 10 + s[9] - '0';
|
|
|
|
|
dateval.tm_hour = (s[10] - '0') * 10 + s[11] - '0';
|
|
|
|
|
dateval.tm_min = (s[12] - '0') * 10 + s[13] - '0';
|
|
|
|
|
dateval.tm_sec = (s[14] - '0') * 10 + s[15] - '0';
|
|
|
|
|
|
|
|
|
|
if (s[16] == 'Z')
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|