libxlsxwriter/src/utility.c in fast_excel-0.4.1 vs libxlsxwriter/src/utility.c in fast_excel-0.5.0

- old
+ new

@@ -1,22 +1,31 @@ /***************************************************************************** * utility - Utility functions for libxlsxwriter. * * Used in conjunction with the libxlsxwriter library. * - * Copyright 2014-2019, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ +#ifdef USE_FMEMOPEN +#define _POSIX_C_SOURCE 200809L +#endif + #include <ctype.h> #include <stdio.h> #include <string.h> #include <stdint.h> #include <stdlib.h> #include "xlsxwriter.h" +#include "xlsxwriter/common.h" #include "xlsxwriter/third_party/tmpfileplus.h" +#ifdef USE_DTOA_LIBRARY +#include "xlsxwriter/third_party/emyg_dtoa.h" +#endif + char *error_strings[LXW_MAX_ERRNO + 1] = { "No error.", "Memory error, failed to malloc() required memory.", "Error creating output xlsx file. Usually a permissions error.", "Error encountered when creating a tmpfile during file assembly.", @@ -25,23 +34,24 @@ "Zip error ZIP_PARAMERROR while creating the xlsx file.", "Zip error ZIP_BADZIPFILE (use_zip64 option may be required).", "Zip error ZIP_INTERNALERROR while creating the xlsx file.", "File error or unknown zip error when adding sub file to xlsx file.", "Unknown zip error when closing xlsx file.", + "Feature is not currently supported in this configuration.", "NULL function parameter ignored.", "Function parameter validation error.", "Worksheet name exceeds Excel's limit of 31 characters.", "Worksheet name cannot contain invalid characters: '[ ] : * ? / \\'", "Worksheet name cannot start or end with an apostrophe.", "Worksheet name is already in use.", - "Worksheet name 'History' is reserved by Excel.", "Parameter exceeds Excel's limit of 32 characters.", "Parameter exceeds Excel's limit of 128 characters.", "Parameter exceeds Excel's limit of 255 characters.", "String exceeds Excel's limit of 32,767 characters.", "Error finding internal string index.", "Worksheet row or column index out of range.", + "Maximum hyperlink length (2079) exceeded.", "Maximum number of worksheet URLs (65530) exceeded.", "Couldn't read image dimensions or DPI.", "Unknown error number." }; @@ -60,11 +70,11 @@ void lxw_col_to_name(char *col_name, lxw_col_t col_num, uint8_t absolute) { uint8_t pos = 0; size_t len; - uint8_t i; + size_t i; /* Change from 0 index to 1 index. */ col_num++; /* Convert the column number to a string in reverse order. */ @@ -310,14 +320,15 @@ else return -1; } /* - * Convert a lxw_datetime struct to an Excel serial date. + * Convert a lxw_datetime struct to an Excel serial date, with a 1900 + * or 1904 epoch. */ double -lxw_datetime_to_excel_date(lxw_datetime *datetime, uint8_t date_1904) +lxw_datetime_to_excel_date_epoch(lxw_datetime *datetime, uint8_t date_1904) { int year = datetime->year; int month = datetime->month; int day = datetime->day; int hour = datetime->hour; @@ -390,11 +401,11 @@ /* Add days for current month. */ days += day; /* Add days for all previous years. */ days += range * 365; /* Add 4 year leapdays. */ - days += (range) / 4; + days += range / 4; /* Remove 100 year leapdays. */ days -= (range + offset) / 100; /* Add 400 year leapdays. */ days += (range + offset + norm) / 400; /* Remove leap days already counted. */ @@ -405,10 +416,47 @@ days++; return days + seconds; } +/* + * Convert a lxw_datetime struct to an Excel serial date, for the 1900 epoch. + */ +double +lxw_datetime_to_excel_datetime(lxw_datetime *datetime) +{ + return lxw_datetime_to_excel_date_epoch(datetime, LXW_FALSE); +} + +/* + * Convert a unix datetime (1970/01/01 epoch) to an Excel serial date, with a + * 1900 epoch. + */ +double +lxw_unixtime_to_excel_date(int64_t unixtime) +{ + return lxw_unixtime_to_excel_date_epoch(unixtime, LXW_FALSE); +} + +/* + * Convert a unix datetime (1970/01/01 epoch) to an Excel serial date, with a + * 1900 or 1904 epoch. + */ +double +lxw_unixtime_to_excel_date_epoch(int64_t unixtime, uint8_t date_1904) +{ + double excel_datetime = 0.0; + double epoch = date_1904 ? 24107.0 : 25568.0; + + excel_datetime = epoch + (unixtime / (24 * 60 * 60.0)); + + if (!date_1904 && excel_datetime >= 60.0) + excel_datetime = excel_datetime + 1.0; + + return excel_datetime; +} + /* Simple strdup() implementation since it isn't ANSI C. */ char * lxw_strdup(const char *str) { size_t len; @@ -522,81 +570,133 @@ /* * Thin wrapper for tmpfile() so it can be over-ridden with a user defined * version if required for safety or portability. */ FILE * -lxw_tmpfile(char *tmpdir) +lxw_tmpfile(const char *tmpdir) { #ifndef USE_STANDARD_TMPFILE return tmpfileplus(tmpdir, NULL, NULL, 0); #else (void) tmpdir; return tmpfile(); #endif } +/** + * Return a memory-backed file if supported, otherwise a temporary one + */ +FILE * +lxw_get_filehandle(char **buf, size_t *size, const char *tmpdir) +{ + static size_t s; + if (!size) + size = &s; + *buf = NULL; + *size = 0; +#ifdef USE_FMEMOPEN + (void) tmpdir; + return open_memstream(buf, size); +#else + return lxw_tmpfile(tmpdir); +#endif +} + /* - * Sample function to handle sprintf of doubles for locale portable code. This - * is usually handled by a lxw_sprintf_dbl() macro but it can be replaced with - * a function of the same name. - * - * The code below is a simplified example that changes numbers like 123,45 to - * 123.45. End-users can replace this with something more rigorous if - * required. + * Use third party function to handle sprintf of doubles for locale portable + * code. */ -#ifdef USE_DOUBLE_FUNCTION +#ifdef USE_DTOA_LIBRARY int lxw_sprintf_dbl(char *data, double number) { - char *tmp; - - lxw_snprintf(data, LXW_ATTR_32, "%.16g", number); - - /* Replace comma with decimal point. */ - tmp = strchr(data, ','); - if (tmp) - *tmp = '.'; - + emyg_dtoa(number, data); return 0; } #endif /* - * Retrieve runtime library version + * Retrieve runtime library version. */ const char * lxw_version(void) { return LXW_VERSION; } /* - * Hash a worksheet password. Based on the algorithm provided by Daniel Rentz - * of OpenOffice. + * Retrieve runtime library version ID. */ uint16_t +lxw_version_id(void) +{ + return LXW_VERSION_ID; +} + +/* + * Hash a worksheet password. Based on the algorithm in ECMA-376-4:2016, + * Office Open XML File Formats - Transitional Migration Features, + * Additional attributes for workbookProtection element (Part 1, ยง18.2.29). + */ +uint16_t lxw_hash_password(const char *password) { - size_t count; - uint8_t i; - uint16_t hash = 0x0000; + uint16_t byte_count = (uint16_t) strlen(password); + uint16_t hash = 0; + const char *p = &password[byte_count]; - count = strlen(password); + if (!byte_count) + return hash; - for (i = 0; i < count; i++) { - uint32_t low_15; - uint32_t high_15; - uint32_t letter = password[i] << (i + 1); - - low_15 = letter & 0x7fff; - high_15 = letter & (0x7fff << 15); - high_15 = high_15 >> 15; - letter = low_15 | high_15; - - hash ^= letter; + while (p-- != password) { + hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7fff); + hash ^= *p & 0xFF; } - hash ^= count; + hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7fff); + hash ^= byte_count; hash ^= 0xCE4B; return hash; } + +/* Make a simple portable version of fopen() for Windows. */ +#ifdef __MINGW32__ +#undef _WIN32 +#endif + +#ifdef _WIN32 + +#include <windows.h> + +FILE * +lxw_fopen(const char *filename, const char *mode) +{ + int n; + wchar_t wide_filename[_MAX_PATH + 1] = L""; + wchar_t wide_mode[_MAX_PATH + 1] = L""; + + n = MultiByteToWideChar(CP_UTF8, 0, filename, (int) strlen(filename), + wide_filename, _MAX_PATH); + + if (n == 0) { + LXW_ERROR("MultiByteToWideChar error: filename"); + return NULL; + } + + n = MultiByteToWideChar(CP_UTF8, 0, mode, (int) strlen(mode), + wide_mode, _MAX_PATH); + + if (n == 0) { + LXW_ERROR("MultiByteToWideChar error: mode"); + return NULL; + } + + return _wfopen(wide_filename, wide_mode); +} +#else +FILE * +lxw_fopen(const char *filename, const char *mode) +{ + return fopen(filename, mode); +} +#endif