fast_excel 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +5 -2
- data/Makefile +14 -0
- data/README.md +7 -2
- data/Rakefile +10 -0
- data/extconf.rb +0 -0
- data/fast_excel.gemspec +3 -1
- data/lib/fast_excel/binding.rb +3 -7
- data/lib/rubygems_plugin.rb +3 -0
- data/libxlsxwriter/.gitignore +49 -0
- data/libxlsxwriter/.indent.pro +125 -0
- data/libxlsxwriter/.travis.yml +25 -0
- data/libxlsxwriter/CONTRIBUTING.md +226 -0
- data/libxlsxwriter/Changes.txt +557 -0
- data/libxlsxwriter/LICENSE.txt +89 -0
- data/libxlsxwriter/Makefile +156 -0
- data/libxlsxwriter/Readme.md +78 -0
- data/libxlsxwriter/cocoapods/libxlsxwriter-umbrella.h +30 -0
- data/libxlsxwriter/cocoapods/libxlsxwriter.modulemap +7 -0
- data/libxlsxwriter/include/xlsxwriter/app.h +79 -0
- data/libxlsxwriter/include/xlsxwriter/chart.h +3476 -0
- data/libxlsxwriter/include/xlsxwriter/common.h +372 -0
- data/libxlsxwriter/include/xlsxwriter/content_types.h +74 -0
- data/libxlsxwriter/include/xlsxwriter/core.h +51 -0
- data/libxlsxwriter/include/xlsxwriter/custom.h +52 -0
- data/libxlsxwriter/include/xlsxwriter/drawing.h +111 -0
- data/libxlsxwriter/include/xlsxwriter/format.h +1214 -0
- data/libxlsxwriter/include/xlsxwriter/hash_table.h +76 -0
- data/libxlsxwriter/include/xlsxwriter/packager.h +80 -0
- data/libxlsxwriter/include/xlsxwriter/relationships.h +77 -0
- data/libxlsxwriter/include/xlsxwriter/shared_strings.h +83 -0
- data/libxlsxwriter/include/xlsxwriter/styles.h +77 -0
- data/libxlsxwriter/include/xlsxwriter/theme.h +47 -0
- data/libxlsxwriter/include/xlsxwriter/third_party/ioapi.h +214 -0
- data/libxlsxwriter/include/xlsxwriter/third_party/queue.h +694 -0
- data/libxlsxwriter/include/xlsxwriter/third_party/tmpfileplus.h +53 -0
- data/libxlsxwriter/include/xlsxwriter/third_party/tree.h +801 -0
- data/libxlsxwriter/include/xlsxwriter/third_party/zip.h +375 -0
- data/libxlsxwriter/include/xlsxwriter/utility.h +166 -0
- data/libxlsxwriter/include/xlsxwriter/workbook.h +757 -0
- data/libxlsxwriter/include/xlsxwriter/worksheet.h +2641 -0
- data/libxlsxwriter/include/xlsxwriter/xmlwriter.h +178 -0
- data/libxlsxwriter/include/xlsxwriter.h +23 -0
- data/libxlsxwriter/lib/.gitignore +0 -0
- data/libxlsxwriter/libxlsxwriter.podspec +47 -0
- data/libxlsxwriter/src/Makefile +130 -0
- data/libxlsxwriter/src/app.c +443 -0
- data/libxlsxwriter/src/chart.c +6346 -0
- data/libxlsxwriter/src/content_types.c +345 -0
- data/libxlsxwriter/src/core.c +293 -0
- data/libxlsxwriter/src/custom.c +224 -0
- data/libxlsxwriter/src/drawing.c +746 -0
- data/libxlsxwriter/src/format.c +729 -0
- data/libxlsxwriter/src/hash_table.c +223 -0
- data/libxlsxwriter/src/packager.c +948 -0
- data/libxlsxwriter/src/relationships.c +245 -0
- data/libxlsxwriter/src/shared_strings.c +266 -0
- data/libxlsxwriter/src/styles.c +1088 -0
- data/libxlsxwriter/src/theme.c +348 -0
- data/libxlsxwriter/src/utility.c +515 -0
- data/libxlsxwriter/src/workbook.c +1930 -0
- data/libxlsxwriter/src/worksheet.c +5022 -0
- data/libxlsxwriter/src/xmlwriter.c +355 -0
- data/libxlsxwriter/third_party/minizip/Makefile +44 -0
- data/libxlsxwriter/third_party/minizip/Makefile.am +45 -0
- data/libxlsxwriter/third_party/minizip/Makefile.orig +25 -0
- data/libxlsxwriter/third_party/minizip/MiniZip64_Changes.txt +6 -0
- data/libxlsxwriter/third_party/minizip/MiniZip64_info.txt +74 -0
- data/libxlsxwriter/third_party/minizip/README.txt +5 -0
- data/libxlsxwriter/third_party/minizip/configure.ac +32 -0
- data/libxlsxwriter/third_party/minizip/crypt.h +131 -0
- data/libxlsxwriter/third_party/minizip/ioapi.c +247 -0
- data/libxlsxwriter/third_party/minizip/ioapi.h +208 -0
- data/libxlsxwriter/third_party/minizip/iowin32.c +456 -0
- data/libxlsxwriter/third_party/minizip/iowin32.h +28 -0
- data/libxlsxwriter/third_party/minizip/make_vms.com +25 -0
- data/libxlsxwriter/third_party/minizip/miniunz.c +660 -0
- data/libxlsxwriter/third_party/minizip/miniunzip.1 +63 -0
- data/libxlsxwriter/third_party/minizip/minizip.1 +46 -0
- data/libxlsxwriter/third_party/minizip/minizip.c +520 -0
- data/libxlsxwriter/third_party/minizip/minizip.pc.in +12 -0
- data/libxlsxwriter/third_party/minizip/mztools.c +291 -0
- data/libxlsxwriter/third_party/minizip/mztools.h +37 -0
- data/libxlsxwriter/third_party/minizip/unzip.c +2125 -0
- data/libxlsxwriter/third_party/minizip/unzip.h +437 -0
- data/libxlsxwriter/third_party/minizip/zip.c +2007 -0
- data/libxlsxwriter/third_party/minizip/zip.h +367 -0
- data/libxlsxwriter/third_party/tmpfileplus/Makefile +42 -0
- data/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.c +342 -0
- data/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.h +53 -0
- data/libxlsxwriter/version.txt +1 -0
- metadata +89 -6
- data/binaries/libxlsxwriter-alpine.so +0 -0
- data/binaries/libxlsxwriter-darwin.dylib +0 -0
- data/binaries/libxlsxwriter-glibc.so +0 -0
@@ -0,0 +1,515 @@
|
|
1
|
+
/*****************************************************************************
|
2
|
+
* utility - Utility functions for libxlsxwriter.
|
3
|
+
*
|
4
|
+
* Used in conjunction with the libxlsxwriter library.
|
5
|
+
*
|
6
|
+
* Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
|
7
|
+
*
|
8
|
+
*/
|
9
|
+
|
10
|
+
#include <ctype.h>
|
11
|
+
#include <stdio.h>
|
12
|
+
#include <string.h>
|
13
|
+
#include <stdint.h>
|
14
|
+
#include <stdlib.h>
|
15
|
+
#include "xlsxwriter/utility.h"
|
16
|
+
#include "xlsxwriter/third_party/tmpfileplus.h"
|
17
|
+
|
18
|
+
char *error_strings[LXW_MAX_ERRNO + 1] = {
|
19
|
+
"No error.",
|
20
|
+
"Memory error, failed to malloc() required memory.",
|
21
|
+
"Error creating output xlsx file. Usually a permissions error.",
|
22
|
+
"Error encountered when creating a tmpfile during file assembly.",
|
23
|
+
"Zlib error with a file operation while creating xlsx file.",
|
24
|
+
"Zlib error when adding sub file to xlsx file.",
|
25
|
+
"Zlib error when closing xlsx file.",
|
26
|
+
"NULL function parameter ignored.",
|
27
|
+
"Function parameter validation error.",
|
28
|
+
"Worksheet name exceeds Excel's limit of 31 characters.",
|
29
|
+
"Worksheet name contains invalid Excel character: '[]:*?/\\'",
|
30
|
+
"Worksheet name is already in use.",
|
31
|
+
"Parameter exceeds Excel's limit of 128 characters.",
|
32
|
+
"Parameter exceeds Excel's limit of 255 characters.",
|
33
|
+
"String exceeds Excel's limit of 32,767 characters.",
|
34
|
+
"Error finding internal string index.",
|
35
|
+
"Worksheet row or column index out of range.",
|
36
|
+
"Maximum number of worksheet URLs (65530) exceeded.",
|
37
|
+
"Couldn't read image dimensions or DPI.",
|
38
|
+
"Unknown error number."
|
39
|
+
};
|
40
|
+
|
41
|
+
char *
|
42
|
+
lxw_strerror(lxw_error error_num)
|
43
|
+
{
|
44
|
+
if (error_num > LXW_MAX_ERRNO)
|
45
|
+
error_num = LXW_MAX_ERRNO;
|
46
|
+
|
47
|
+
return error_strings[error_num];
|
48
|
+
}
|
49
|
+
|
50
|
+
/*
|
51
|
+
* Convert Excel A-XFD style column name to zero based number.
|
52
|
+
*/
|
53
|
+
void
|
54
|
+
lxw_col_to_name(char *col_name, lxw_col_t col_num, uint8_t absolute)
|
55
|
+
{
|
56
|
+
uint8_t pos = 0;
|
57
|
+
size_t len;
|
58
|
+
uint8_t i;
|
59
|
+
|
60
|
+
/* Change from 0 index to 1 index. */
|
61
|
+
col_num++;
|
62
|
+
|
63
|
+
/* Convert the column number to a string in reverse order. */
|
64
|
+
while (col_num) {
|
65
|
+
|
66
|
+
/* Get the remainder in base 26. */
|
67
|
+
int remainder = col_num % 26;
|
68
|
+
|
69
|
+
if (remainder == 0)
|
70
|
+
remainder = 26;
|
71
|
+
|
72
|
+
/* Convert the remainder value to a character. */
|
73
|
+
col_name[pos++] = 'A' + remainder - 1;
|
74
|
+
col_name[pos] = '\0';
|
75
|
+
|
76
|
+
/* Get the next order of magnitude. */
|
77
|
+
col_num = (col_num - 1) / 26;
|
78
|
+
}
|
79
|
+
|
80
|
+
if (absolute) {
|
81
|
+
col_name[pos] = '$';
|
82
|
+
col_name[pos + 1] = '\0';
|
83
|
+
}
|
84
|
+
|
85
|
+
/* Reverse the column name string. */
|
86
|
+
len = strlen(col_name);
|
87
|
+
for (i = 0; i < (len / 2); i++) {
|
88
|
+
char tmp = col_name[i];
|
89
|
+
col_name[i] = col_name[len - i - 1];
|
90
|
+
col_name[len - i - 1] = tmp;
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
/*
|
95
|
+
* Convert zero indexed row and column to an Excel style A1 cell reference.
|
96
|
+
*/
|
97
|
+
void
|
98
|
+
lxw_rowcol_to_cell(char *cell_name, lxw_row_t row, lxw_col_t col)
|
99
|
+
{
|
100
|
+
size_t pos;
|
101
|
+
|
102
|
+
/* Add the column to the cell. */
|
103
|
+
lxw_col_to_name(cell_name, col, 0);
|
104
|
+
|
105
|
+
/* Get the end of the cell. */
|
106
|
+
pos = strlen(cell_name);
|
107
|
+
|
108
|
+
/* Add the row to the cell. */
|
109
|
+
lxw_snprintf(&cell_name[pos], LXW_MAX_ROW_NAME_LENGTH, "%d", ++row);
|
110
|
+
}
|
111
|
+
|
112
|
+
/*
|
113
|
+
* Convert zero indexed row and column to an Excel style $A$1 cell with
|
114
|
+
* an absolute reference.
|
115
|
+
*/
|
116
|
+
void
|
117
|
+
lxw_rowcol_to_cell_abs(char *cell_name, lxw_row_t row, lxw_col_t col,
|
118
|
+
uint8_t abs_row, uint8_t abs_col)
|
119
|
+
{
|
120
|
+
size_t pos;
|
121
|
+
|
122
|
+
/* Add the column to the cell. */
|
123
|
+
lxw_col_to_name(cell_name, col, abs_col);
|
124
|
+
|
125
|
+
/* Get the end of the cell. */
|
126
|
+
pos = strlen(cell_name);
|
127
|
+
|
128
|
+
if (abs_row)
|
129
|
+
cell_name[pos++] = '$';
|
130
|
+
|
131
|
+
/* Add the row to the cell. */
|
132
|
+
lxw_snprintf(&cell_name[pos], LXW_MAX_ROW_NAME_LENGTH, "%d", ++row);
|
133
|
+
}
|
134
|
+
|
135
|
+
/*
|
136
|
+
* Convert zero indexed row and column pair to an Excel style A1:C5
|
137
|
+
* range reference.
|
138
|
+
*/
|
139
|
+
void
|
140
|
+
lxw_rowcol_to_range(char *range,
|
141
|
+
lxw_row_t first_row, lxw_col_t first_col,
|
142
|
+
lxw_row_t last_row, lxw_col_t last_col)
|
143
|
+
{
|
144
|
+
size_t pos;
|
145
|
+
|
146
|
+
/* Add the first cell to the range. */
|
147
|
+
lxw_rowcol_to_cell(range, first_row, first_col);
|
148
|
+
|
149
|
+
/* If the start and end cells are the same just return a single cell. */
|
150
|
+
if (first_row == last_row && first_col == last_col)
|
151
|
+
return;
|
152
|
+
|
153
|
+
/* Get the end of the cell. */
|
154
|
+
pos = strlen(range);
|
155
|
+
|
156
|
+
/* Add the range separator. */
|
157
|
+
range[pos++] = ':';
|
158
|
+
|
159
|
+
/* Add the first cell to the range. */
|
160
|
+
lxw_rowcol_to_cell(&range[pos], last_row, last_col);
|
161
|
+
}
|
162
|
+
|
163
|
+
/*
|
164
|
+
* Convert zero indexed row and column pairs to an Excel style $A$1:$C$5
|
165
|
+
* range reference with absolute values.
|
166
|
+
*/
|
167
|
+
void
|
168
|
+
lxw_rowcol_to_range_abs(char *range,
|
169
|
+
lxw_row_t first_row, lxw_col_t first_col,
|
170
|
+
lxw_row_t last_row, lxw_col_t last_col)
|
171
|
+
{
|
172
|
+
size_t pos;
|
173
|
+
|
174
|
+
/* Add the first cell to the range. */
|
175
|
+
lxw_rowcol_to_cell_abs(range, first_row, first_col, 1, 1);
|
176
|
+
|
177
|
+
/* If the start and end cells are the same just return a single cell. */
|
178
|
+
if (first_row == last_row && first_col == last_col)
|
179
|
+
return;
|
180
|
+
|
181
|
+
/* Get the end of the cell. */
|
182
|
+
pos = strlen(range);
|
183
|
+
|
184
|
+
/* Add the range separator. */
|
185
|
+
range[pos++] = ':';
|
186
|
+
|
187
|
+
/* Add the first cell to the range. */
|
188
|
+
lxw_rowcol_to_cell_abs(&range[pos], last_row, last_col, 1, 1);
|
189
|
+
}
|
190
|
+
|
191
|
+
/*
|
192
|
+
* Convert sheetname and zero indexed row and column pairs to an Excel style
|
193
|
+
* Sheet1!$A$1:$C$5 formula reference with absolute values.
|
194
|
+
*/
|
195
|
+
void
|
196
|
+
lxw_rowcol_to_formula_abs(char *formula, const char *sheetname,
|
197
|
+
lxw_row_t first_row, lxw_col_t first_col,
|
198
|
+
lxw_row_t last_row, lxw_col_t last_col)
|
199
|
+
{
|
200
|
+
size_t pos;
|
201
|
+
char *quoted_name = lxw_quote_sheetname(sheetname);
|
202
|
+
|
203
|
+
strncpy(formula, quoted_name, LXW_MAX_FORMULA_RANGE_LENGTH - 1);
|
204
|
+
free(quoted_name);
|
205
|
+
|
206
|
+
/* Get the end of the sheetname. */
|
207
|
+
pos = strlen(formula);
|
208
|
+
|
209
|
+
/* Add the range separator. */
|
210
|
+
formula[pos++] = '!';
|
211
|
+
|
212
|
+
/* Add the first cell to the range. */
|
213
|
+
lxw_rowcol_to_cell_abs(&formula[pos], first_row, first_col, 1, 1);
|
214
|
+
|
215
|
+
/* If the start and end cells are the same just return a single cell. */
|
216
|
+
if (first_row == last_row && first_col == last_col)
|
217
|
+
return;
|
218
|
+
|
219
|
+
/* Get the end of the cell. */
|
220
|
+
pos = strlen(formula);
|
221
|
+
|
222
|
+
/* Add the range separator. */
|
223
|
+
formula[pos++] = ':';
|
224
|
+
|
225
|
+
/* Add the first cell to the range. */
|
226
|
+
lxw_rowcol_to_cell_abs(&formula[pos], last_row, last_col, 1, 1);
|
227
|
+
}
|
228
|
+
|
229
|
+
/*
|
230
|
+
* Convert an Excel style A1 cell reference to a zero indexed row number.
|
231
|
+
*/
|
232
|
+
lxw_row_t
|
233
|
+
lxw_name_to_row(const char *row_str)
|
234
|
+
{
|
235
|
+
lxw_row_t row_num = 0;
|
236
|
+
const char *p = row_str;
|
237
|
+
|
238
|
+
/* Skip the column letters and absolute symbol of the A1 cell. */
|
239
|
+
while (p && !isdigit((unsigned char) *p))
|
240
|
+
p++;
|
241
|
+
|
242
|
+
/* Convert the row part of the A1 cell to a number. */
|
243
|
+
if (p)
|
244
|
+
row_num = atoi(p);
|
245
|
+
|
246
|
+
if (row_num)
|
247
|
+
return row_num - 1;
|
248
|
+
else
|
249
|
+
return 0;
|
250
|
+
}
|
251
|
+
|
252
|
+
/*
|
253
|
+
* Convert an Excel style A1 cell reference to a zero indexed column number.
|
254
|
+
*/
|
255
|
+
lxw_col_t
|
256
|
+
lxw_name_to_col(const char *col_str)
|
257
|
+
{
|
258
|
+
lxw_col_t col_num = 0;
|
259
|
+
const char *p = col_str;
|
260
|
+
|
261
|
+
/* Convert leading column letters of A1 cell. Ignore absolute $ marker. */
|
262
|
+
while (p && (isupper((unsigned char) *p) || *p == '$')) {
|
263
|
+
if (*p != '$')
|
264
|
+
col_num = (col_num * 26) + (*p - 'A' + 1);
|
265
|
+
p++;
|
266
|
+
}
|
267
|
+
|
268
|
+
return col_num - 1;
|
269
|
+
}
|
270
|
+
|
271
|
+
/*
|
272
|
+
* Convert the second row of an Excel range ref to a zero indexed number.
|
273
|
+
*/
|
274
|
+
uint32_t
|
275
|
+
lxw_name_to_row_2(const char *row_str)
|
276
|
+
{
|
277
|
+
const char *p = row_str;
|
278
|
+
|
279
|
+
/* Find the : separator in the range. */
|
280
|
+
while (p && *p != ':')
|
281
|
+
p++;
|
282
|
+
|
283
|
+
if (p)
|
284
|
+
return lxw_name_to_row(++p);
|
285
|
+
else
|
286
|
+
return -1;
|
287
|
+
}
|
288
|
+
|
289
|
+
/*
|
290
|
+
* Convert the second column of an Excel range ref to a zero indexed number.
|
291
|
+
*/
|
292
|
+
uint16_t
|
293
|
+
lxw_name_to_col_2(const char *col_str)
|
294
|
+
{
|
295
|
+
const char *p = col_str;
|
296
|
+
|
297
|
+
/* Find the : separator in the range. */
|
298
|
+
while (p && *p != ':')
|
299
|
+
p++;
|
300
|
+
|
301
|
+
if (p)
|
302
|
+
return lxw_name_to_col(++p);
|
303
|
+
else
|
304
|
+
return -1;
|
305
|
+
}
|
306
|
+
|
307
|
+
/*
|
308
|
+
* Convert a lxw_datetime struct to an Excel serial date.
|
309
|
+
*/
|
310
|
+
double
|
311
|
+
lxw_datetime_to_excel_date(lxw_datetime *datetime, uint8_t date_1904)
|
312
|
+
{
|
313
|
+
int year = datetime->year;
|
314
|
+
int month = datetime->month;
|
315
|
+
int day = datetime->day;
|
316
|
+
int hour = datetime->hour;
|
317
|
+
int min = datetime->min;
|
318
|
+
double sec = datetime->sec;
|
319
|
+
double seconds;
|
320
|
+
int epoch = date_1904 ? 1904 : 1900;
|
321
|
+
int offset = date_1904 ? 4 : 0;
|
322
|
+
int norm = 300;
|
323
|
+
int range;
|
324
|
+
/* Set month days and check for leap year. */
|
325
|
+
int mdays[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
326
|
+
int leap = 0;
|
327
|
+
int days = 0;
|
328
|
+
int i;
|
329
|
+
|
330
|
+
/* For times without dates set the default date for the epoch. */
|
331
|
+
if (!year) {
|
332
|
+
if (!date_1904) {
|
333
|
+
year = 1899;
|
334
|
+
month = 12;
|
335
|
+
day = 31;
|
336
|
+
}
|
337
|
+
else {
|
338
|
+
year = 1904;
|
339
|
+
month = 1;
|
340
|
+
day = 1;
|
341
|
+
}
|
342
|
+
}
|
343
|
+
|
344
|
+
/* Convert the Excel seconds to a fraction of the seconds in 24 hours. */
|
345
|
+
seconds = (hour * 60 * 60 + min * 60 + sec) / (24 * 60 * 60.0);
|
346
|
+
|
347
|
+
/* Special cases for Excel dates in the 1900 epoch. */
|
348
|
+
if (!date_1904) {
|
349
|
+
/* Excel 1900 epoch. */
|
350
|
+
if (year == 1899 && month == 12 && day == 31)
|
351
|
+
return seconds;
|
352
|
+
|
353
|
+
/* Excel 1900 epoch. */
|
354
|
+
if (year == 1900 && month == 1 && day == 0)
|
355
|
+
return seconds;
|
356
|
+
|
357
|
+
/* Excel false leapday */
|
358
|
+
if (year == 1900 && month == 2 && day == 29)
|
359
|
+
return 60 + seconds;
|
360
|
+
}
|
361
|
+
|
362
|
+
/* We calculate the date by calculating the number of days since the */
|
363
|
+
/* epoch and adjust for the number of leap days. We calculate the */
|
364
|
+
/* number of leap days by normalizing the year in relation to the */
|
365
|
+
/* epoch. Thus the year 2000 becomes 100 for 4-year and 100-year */
|
366
|
+
/* leapdays and 400 for 400-year leapdays. */
|
367
|
+
range = year - epoch;
|
368
|
+
|
369
|
+
if (year % 4 == 0 && (year % 100 > 0 || year % 400 == 0)) {
|
370
|
+
leap = 1;
|
371
|
+
mdays[2] = 29;
|
372
|
+
}
|
373
|
+
|
374
|
+
/*
|
375
|
+
* Calculate the serial date by accumulating the number of days
|
376
|
+
* since the epoch.
|
377
|
+
*/
|
378
|
+
|
379
|
+
/* Add days for previous months. */
|
380
|
+
for (i = 0; i < month; i++) {
|
381
|
+
days += mdays[i];
|
382
|
+
}
|
383
|
+
/* Add days for current month. */
|
384
|
+
days += day;
|
385
|
+
/* Add days for all previous years. */
|
386
|
+
days += range * 365;
|
387
|
+
/* Add 4 year leapdays. */
|
388
|
+
days += (range) / 4;
|
389
|
+
/* Remove 100 year leapdays. */
|
390
|
+
days -= (range + offset) / 100;
|
391
|
+
/* Add 400 year leapdays. */
|
392
|
+
days += (range + offset + norm) / 400;
|
393
|
+
/* Remove leap days already counted. */
|
394
|
+
days -= leap;
|
395
|
+
|
396
|
+
/* Adjust for Excel erroneously treating 1900 as a leap year. */
|
397
|
+
if (!date_1904 && days > 59)
|
398
|
+
days++;
|
399
|
+
|
400
|
+
return days + seconds;
|
401
|
+
}
|
402
|
+
|
403
|
+
/* Simple strdup() implementation since it isn't ANSI C. */
|
404
|
+
char *
|
405
|
+
lxw_strdup(const char *str)
|
406
|
+
{
|
407
|
+
size_t len;
|
408
|
+
char *copy;
|
409
|
+
|
410
|
+
if (!str)
|
411
|
+
return NULL;
|
412
|
+
|
413
|
+
len = strlen(str) + 1;
|
414
|
+
copy = malloc(len);
|
415
|
+
|
416
|
+
if (copy)
|
417
|
+
memcpy(copy, str, len);
|
418
|
+
|
419
|
+
return copy;
|
420
|
+
}
|
421
|
+
|
422
|
+
/* Simple strlen that counts UTF-8 characters. Assumes well formed UTF-8. */
|
423
|
+
size_t
|
424
|
+
lxw_utf8_strlen(const char *str)
|
425
|
+
{
|
426
|
+
size_t byte_count = 0;
|
427
|
+
size_t char_count = 0;
|
428
|
+
|
429
|
+
while (str[byte_count]) {
|
430
|
+
if ((str[byte_count] & 0xc0) != 0x80)
|
431
|
+
char_count++;
|
432
|
+
|
433
|
+
byte_count++;
|
434
|
+
}
|
435
|
+
|
436
|
+
return char_count;
|
437
|
+
}
|
438
|
+
|
439
|
+
/* Simple tolower() for strings. */
|
440
|
+
void
|
441
|
+
lxw_str_tolower(char *str)
|
442
|
+
{
|
443
|
+
int i;
|
444
|
+
|
445
|
+
for (i = 0; str[i]; i++)
|
446
|
+
str[i] = tolower(str[i]);
|
447
|
+
}
|
448
|
+
|
449
|
+
/* Create a quoted version of the worksheet name, or return an unmodified
|
450
|
+
* copy if it doesn't required quoting. */
|
451
|
+
char *
|
452
|
+
lxw_quote_sheetname(const char *str)
|
453
|
+
{
|
454
|
+
|
455
|
+
uint8_t needs_quoting = 0;
|
456
|
+
size_t number_of_quotes = 2;
|
457
|
+
size_t i, j;
|
458
|
+
size_t len = strlen(str);
|
459
|
+
|
460
|
+
/* Don't quote the sheetname if it is already quoted. */
|
461
|
+
if (str[0] == '\'')
|
462
|
+
return lxw_strdup(str);
|
463
|
+
|
464
|
+
/* Check if the sheetname contains any characters that require it
|
465
|
+
* to be quoted. Also check for single quotes within the string. */
|
466
|
+
for (i = 0; i < len; i++) {
|
467
|
+
if (!isalnum((unsigned char) str[i]) && str[i] != '_'
|
468
|
+
&& str[i] != '.')
|
469
|
+
needs_quoting = 1;
|
470
|
+
|
471
|
+
if (str[i] == '\'') {
|
472
|
+
needs_quoting = 1;
|
473
|
+
number_of_quotes++;
|
474
|
+
}
|
475
|
+
}
|
476
|
+
|
477
|
+
if (!needs_quoting) {
|
478
|
+
return lxw_strdup(str);
|
479
|
+
}
|
480
|
+
else {
|
481
|
+
/* Add single quotes to the start and end of the string. */
|
482
|
+
char *quoted_name = calloc(1, len + number_of_quotes + 1);
|
483
|
+
RETURN_ON_MEM_ERROR(quoted_name, NULL);
|
484
|
+
|
485
|
+
quoted_name[0] = '\'';
|
486
|
+
|
487
|
+
for (i = 0, j = 1; i < len; i++, j++) {
|
488
|
+
quoted_name[j] = str[i];
|
489
|
+
|
490
|
+
/* Double quote inline single quotes. */
|
491
|
+
if (str[i] == '\'') {
|
492
|
+
quoted_name[++j] = '\'';
|
493
|
+
}
|
494
|
+
}
|
495
|
+
quoted_name[j++] = '\'';
|
496
|
+
quoted_name[j++] = '\0';
|
497
|
+
|
498
|
+
return quoted_name;
|
499
|
+
}
|
500
|
+
}
|
501
|
+
|
502
|
+
/*
|
503
|
+
* Thin wrapper for tmpfile() so it can be over-ridden with a user defined
|
504
|
+
* version if required for safety or portability.
|
505
|
+
*/
|
506
|
+
FILE *
|
507
|
+
lxw_tmpfile(char *tmpdir)
|
508
|
+
{
|
509
|
+
#ifndef USE_STANDARD_TMPFILE
|
510
|
+
return tmpfileplus(tmpdir, NULL, NULL, 0);
|
511
|
+
#else
|
512
|
+
(void) tmpdir;
|
513
|
+
return tmpfile();
|
514
|
+
#endif
|
515
|
+
}
|