xlsxwriter 0.0.3 → 0.0.4.pre.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/ext/xlsxwriter/libxlsxwriter/{LICENSE.txt → License.txt} +64 -3
  3. data/ext/xlsxwriter/libxlsxwriter/Makefile +42 -7
  4. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter.h +1 -1
  5. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/app.h +2 -2
  6. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/chart.h +2481 -97
  7. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/common.h +41 -2
  8. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/core.h +1 -1
  9. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/format.h +5 -5
  10. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/packager.h +8 -3
  11. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/styles.h +1 -1
  12. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/utility.h +1 -0
  13. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/workbook.h +2 -2
  14. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/worksheet.h +381 -1
  15. data/ext/xlsxwriter/libxlsxwriter/src/Makefile +25 -5
  16. data/ext/xlsxwriter/libxlsxwriter/src/app.c +17 -13
  17. data/ext/xlsxwriter/libxlsxwriter/src/chart.c +3903 -976
  18. data/ext/xlsxwriter/libxlsxwriter/src/content_types.c +18 -14
  19. data/ext/xlsxwriter/libxlsxwriter/src/core.c +9 -9
  20. data/ext/xlsxwriter/libxlsxwriter/src/format.c +2 -2
  21. data/ext/xlsxwriter/libxlsxwriter/src/packager.c +179 -95
  22. data/ext/xlsxwriter/libxlsxwriter/src/relationships.c +11 -8
  23. data/ext/xlsxwriter/libxlsxwriter/src/shared_strings.c +2 -0
  24. data/ext/xlsxwriter/libxlsxwriter/src/styles.c +10 -8
  25. data/ext/xlsxwriter/libxlsxwriter/src/utility.c +18 -1
  26. data/ext/xlsxwriter/libxlsxwriter/src/workbook.c +41 -25
  27. data/ext/xlsxwriter/libxlsxwriter/src/worksheet.c +672 -42
  28. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/Makefile +6 -1
  29. data/lib/xlsxwriter/version.rb +1 -1
  30. data/lib/xlsxwriter/worksheet.rb +11 -5
  31. metadata +6 -6
@@ -53,16 +53,19 @@ lxw_free_relationships(lxw_relationships *rels)
53
53
  if (!rels)
54
54
  return;
55
55
 
56
- while (!STAILQ_EMPTY(rels->relationships)) {
57
- relationship = STAILQ_FIRST(rels->relationships);
58
- STAILQ_REMOVE_HEAD(rels->relationships, list_pointers);
59
- free(relationship->type);
60
- free(relationship->target);
61
- free(relationship->target_mode);
62
- free(relationship);
56
+ if (rels->relationships) {
57
+ while (!STAILQ_EMPTY(rels->relationships)) {
58
+ relationship = STAILQ_FIRST(rels->relationships);
59
+ STAILQ_REMOVE_HEAD(rels->relationships, list_pointers);
60
+ free(relationship->type);
61
+ free(relationship->target);
62
+ free(relationship->target_mode);
63
+ free(relationship);
64
+ }
65
+
66
+ free(rels->relationships);
63
67
  }
64
68
 
65
- free(rels->relationships);
66
69
  free(rels);
67
70
  }
68
71
 
@@ -19,8 +19,10 @@
19
19
  STATIC int _element_cmp(struct sst_element *element1,
20
20
  struct sst_element *element2);
21
21
 
22
+ #ifndef __clang_analyzer__
22
23
  LXW_RB_GENERATE_ELEMENT(sst_rb_tree, sst_element, sst_tree_pointers,
23
24
  _element_cmp);
25
+ #endif
24
26
 
25
27
  /*****************************************************************************
26
28
  *
@@ -54,13 +54,15 @@ lxw_styles_free(lxw_styles *styles)
54
54
  return;
55
55
 
56
56
  /* Free the formats in the styles. */
57
- while (!STAILQ_EMPTY(styles->xf_formats)) {
58
- format = STAILQ_FIRST(styles->xf_formats);
59
- STAILQ_REMOVE_HEAD(styles->xf_formats, list_pointers);
60
- free(format);
57
+ if (styles->xf_formats) {
58
+ while (!STAILQ_EMPTY(styles->xf_formats)) {
59
+ format = STAILQ_FIRST(styles->xf_formats);
60
+ STAILQ_REMOVE_HEAD(styles->xf_formats, list_pointers);
61
+ free(format);
62
+ }
63
+ free(styles->xf_formats);
61
64
  }
62
65
 
63
- free(styles->xf_formats);
64
66
  free(styles);
65
67
  }
66
68
 
@@ -151,13 +153,13 @@ _write_num_fmts(lxw_styles *self)
151
153
  * Write the <sz> element.
152
154
  */
153
155
  STATIC void
154
- _write_font_size(lxw_styles *self, uint16_t font_size)
156
+ _write_font_size(lxw_styles *self, double font_size)
155
157
  {
156
158
  struct xml_attribute_list attributes;
157
159
  struct xml_attribute *attribute;
158
160
 
159
161
  LXW_INIT_ATTRIBUTES();
160
- LXW_PUSH_ATTRIBUTES_INT("val", font_size);
162
+ LXW_PUSH_ATTRIBUTES_DBL("val", font_size);
161
163
 
162
164
  lxw_xml_empty_tag(self->file, "sz", &attributes);
163
165
 
@@ -335,7 +337,7 @@ _write_font(lxw_styles *self, lxw_format *format)
335
337
  if (format->font_script == LXW_FONT_SUBSCRIPT)
336
338
  _write_vert_align(self, "subscript");
337
339
 
338
- if (format->font_size)
340
+ if (format->font_size > 0.0)
339
341
  _write_font_size(self, format->font_size);
340
342
 
341
343
  if (format->theme)
@@ -28,6 +28,7 @@ char *error_strings[LXW_MAX_ERRNO + 1] = {
28
28
  "Worksheet name exceeds Excel's limit of 31 characters.",
29
29
  "Worksheet name contains invalid Excel character: '[]:*?/\\'",
30
30
  "Worksheet name is already in use.",
31
+ "Parameter exceeds Excel's limit of 32 characters.",
31
32
  "Parameter exceeds Excel's limit of 128 characters.",
32
33
  "Parameter exceeds Excel's limit of 255 characters.",
33
34
  "String exceeds Excel's limit of 32,767 characters.",
@@ -243,7 +244,10 @@ lxw_name_to_row(const char *row_str)
243
244
  if (p)
244
245
  row_num = atoi(p);
245
246
 
246
- return row_num - 1;
247
+ if (row_num)
248
+ return row_num - 1;
249
+ else
250
+ return 0;
247
251
  }
248
252
 
249
253
  /*
@@ -416,6 +420,19 @@ lxw_strdup(const char *str)
416
420
  return copy;
417
421
  }
418
422
 
423
+ /* Simple function to strdup() a formula string without the leading "=". */
424
+ char *
425
+ lxw_strdup_formula(const char *formula)
426
+ {
427
+ if (!formula)
428
+ return NULL;
429
+
430
+ if (formula[0] == '=')
431
+ return lxw_strdup(formula + 1);
432
+ else
433
+ return lxw_strdup(formula);
434
+ }
435
+
419
436
  /* Simple strlen that counts UTF-8 characters. Assumes well formed UTF-8. */
420
437
  size_t
421
438
  lxw_utf8_strlen(const char *str)
@@ -14,8 +14,10 @@
14
14
  #include "xlsxwriter/hash_table.h"
15
15
 
16
16
  STATIC int _name_cmp(lxw_worksheet_name *name1, lxw_worksheet_name *name2);
17
+ #ifndef __clang_analyzer__
17
18
  LXW_RB_GENERATE_NAMES(lxw_worksheet_names, lxw_worksheet_name, tree_pointers,
18
19
  _name_cmp);
20
+ #endif
19
21
 
20
22
  /*
21
23
  * Forward declarations.
@@ -85,6 +87,7 @@ lxw_workbook_free(lxw_workbook *workbook)
85
87
  lxw_chart *chart;
86
88
  lxw_format *format;
87
89
  lxw_defined_name *defined_name;
90
+ lxw_defined_name *defined_name_tmp;
88
91
  lxw_custom_property *custom_property;
89
92
 
90
93
  if (!workbook)
@@ -95,38 +98,56 @@ lxw_workbook_free(lxw_workbook *workbook)
95
98
  free(workbook->filename);
96
99
 
97
100
  /* Free the worksheets in the workbook. */
98
- while (!STAILQ_EMPTY(workbook->worksheets)) {
99
- worksheet = STAILQ_FIRST(workbook->worksheets);
100
- STAILQ_REMOVE_HEAD(workbook->worksheets, list_pointers);
101
- lxw_worksheet_free(worksheet);
101
+ if (workbook->worksheets) {
102
+ while (!STAILQ_EMPTY(workbook->worksheets)) {
103
+ worksheet = STAILQ_FIRST(workbook->worksheets);
104
+ STAILQ_REMOVE_HEAD(workbook->worksheets, list_pointers);
105
+ lxw_worksheet_free(worksheet);
106
+ }
107
+ free(workbook->worksheets);
108
+
102
109
  }
103
110
 
104
111
  /* Free the charts in the workbook. */
105
- while (!STAILQ_EMPTY(workbook->charts)) {
106
- chart = STAILQ_FIRST(workbook->charts);
107
- STAILQ_REMOVE_HEAD(workbook->charts, list_pointers);
108
- lxw_chart_free(chart);
112
+ if (workbook->charts) {
113
+ while (!STAILQ_EMPTY(workbook->charts)) {
114
+ chart = STAILQ_FIRST(workbook->charts);
115
+ STAILQ_REMOVE_HEAD(workbook->charts, list_pointers);
116
+ lxw_chart_free(chart);
117
+ }
118
+ free(workbook->charts);
109
119
  }
110
120
 
111
121
  /* Free the formats in the workbook. */
112
- while (!STAILQ_EMPTY(workbook->formats)) {
113
- format = STAILQ_FIRST(workbook->formats);
114
- STAILQ_REMOVE_HEAD(workbook->formats, list_pointers);
115
- lxw_format_free(format);
122
+ if (workbook->formats) {
123
+ while (!STAILQ_EMPTY(workbook->formats)) {
124
+ format = STAILQ_FIRST(workbook->formats);
125
+ STAILQ_REMOVE_HEAD(workbook->formats, list_pointers);
126
+ lxw_format_free(format);
127
+ }
128
+ free(workbook->formats);
116
129
  }
117
130
 
118
131
  /* Free the defined_names in the workbook. */
119
- while (!TAILQ_EMPTY(workbook->defined_names)) {
132
+ if (workbook->defined_names) {
120
133
  defined_name = TAILQ_FIRST(workbook->defined_names);
121
- TAILQ_REMOVE(workbook->defined_names, defined_name, list_pointers);
122
- free(defined_name);
134
+ while (defined_name) {
135
+
136
+ defined_name_tmp = TAILQ_NEXT(defined_name, list_pointers);
137
+ free(defined_name);
138
+ defined_name = defined_name_tmp;
139
+ }
140
+ free(workbook->defined_names);
123
141
  }
124
142
 
125
143
  /* Free the custom_properties in the workbook. */
126
- while (!STAILQ_EMPTY(workbook->custom_properties)) {
127
- custom_property = STAILQ_FIRST(workbook->custom_properties);
128
- STAILQ_REMOVE_HEAD(workbook->custom_properties, list_pointers);
129
- _free_custom_doc_property(custom_property);
144
+ if (workbook->custom_properties) {
145
+ while (!STAILQ_EMPTY(workbook->custom_properties)) {
146
+ custom_property = STAILQ_FIRST(workbook->custom_properties);
147
+ STAILQ_REMOVE_HEAD(workbook->custom_properties, list_pointers);
148
+ _free_custom_doc_property(custom_property);
149
+ }
150
+ free(workbook->custom_properties);
130
151
  }
131
152
 
132
153
  if (workbook->worksheet_names) {
@@ -147,12 +168,7 @@ lxw_workbook_free(lxw_workbook *workbook)
147
168
  lxw_hash_free(workbook->used_xf_formats);
148
169
  lxw_sst_free(workbook->sst);
149
170
  free(workbook->options.tmpdir);
150
- free(workbook->worksheets);
151
- free(workbook->charts);
152
171
  free(workbook->ordered_charts);
153
- free(workbook->formats);
154
- free(workbook->defined_names);
155
- free(workbook->custom_properties);
156
172
  free(workbook);
157
173
  }
158
174
 
@@ -1493,7 +1509,7 @@ workbook_close(lxw_workbook *self)
1493
1509
  {
1494
1510
  lxw_worksheet *worksheet = NULL;
1495
1511
  lxw_packager *packager = NULL;
1496
- uint8_t error = LXW_NO_ERROR;
1512
+ lxw_error error = LXW_NO_ERROR;
1497
1513
 
1498
1514
  /* Add a default worksheet if non have been added. */
1499
1515
  if (!self->num_sheets)
@@ -15,11 +15,13 @@
15
15
  #include "xlsxwriter/utility.h"
16
16
  #include "xlsxwriter/relationships.h"
17
17
 
18
- #define LXW_STR_MAX 32767
19
- #define LXW_BUFFER_SIZE 4096
20
- #define LXW_PORTRAIT 1
21
- #define LXW_LANDSCAPE 0
22
- #define LXW_PRINT_ACROSS 1
18
+ #define LXW_STR_MAX 32767
19
+ #define LXW_BUFFER_SIZE 4096
20
+ #define LXW_PORTRAIT 1
21
+ #define LXW_LANDSCAPE 0
22
+ #define LXW_PRINT_ACROSS 1
23
+ #define LXW_VALIDATION_MAX_TITLE_LENGTH 32
24
+ #define LXW_VALIDATION_MAX_STRING_LENGTH 255
23
25
 
24
26
  /*
25
27
  * Forward declarations.
@@ -28,8 +30,10 @@ STATIC void _worksheet_write_rows(lxw_worksheet *self);
28
30
  STATIC int _row_cmp(lxw_row *row1, lxw_row *row2);
29
31
  STATIC int _cell_cmp(lxw_cell *cell1, lxw_cell *cell2);
30
32
 
33
+ #ifndef __clang_analyzer__
31
34
  LXW_RB_GENERATE_ROW(lxw_table_rows, lxw_row, tree_pointers, _row_cmp);
32
35
  LXW_RB_GENERATE_CELL(lxw_table_cells, lxw_cell, tree_pointers, _cell_cmp);
36
+ #endif
33
37
 
34
38
  /*****************************************************************************
35
39
  *
@@ -121,6 +125,11 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data)
121
125
  GOTO_LABEL_ON_MEM_ERROR(worksheet->selections, mem_error);
122
126
  STAILQ_INIT(worksheet->selections);
123
127
 
128
+ worksheet->data_validations =
129
+ calloc(1, sizeof(struct lxw_data_validations));
130
+ GOTO_LABEL_ON_MEM_ERROR(worksheet->data_validations, mem_error);
131
+ STAILQ_INIT(worksheet->data_validations);
132
+
124
133
  worksheet->external_hyperlinks = calloc(1, sizeof(struct lxw_rel_tuples));
125
134
  GOTO_LABEL_ON_MEM_ERROR(worksheet->external_hyperlinks, mem_error);
126
135
  STAILQ_INIT(worksheet->external_hyperlinks);
@@ -137,11 +146,7 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data)
137
146
  if (init_data && init_data->optimize) {
138
147
  FILE *tmpfile;
139
148
 
140
- if (init_data)
141
- tmpfile = lxw_tmpfile(init_data->tmpdir);
142
- else
143
- tmpfile = lxw_tmpfile(NULL);
144
-
149
+ tmpfile = lxw_tmpfile(init_data->tmpdir);
145
150
  if (!tmpfile) {
146
151
  LXW_ERROR("Error creating tmpfile() for worksheet in "
147
152
  "'constant_memory' mode.");
@@ -270,6 +275,26 @@ _free_image_options(lxw_image_options *image)
270
275
  free(image);
271
276
  }
272
277
 
278
+ /*
279
+ * Free a worksheet data_validation.
280
+ */
281
+ STATIC void
282
+ _free_data_validation(lxw_data_validation *data_validation)
283
+ {
284
+ if (!data_validation)
285
+ return;
286
+
287
+ free(data_validation->value_formula);
288
+ free(data_validation->maximum_formula);
289
+ free(data_validation->input_title);
290
+ free(data_validation->input_message);
291
+ free(data_validation->error_title);
292
+ free(data_validation->error_message);
293
+ free(data_validation->minimum_formula);
294
+
295
+ free(data_validation);
296
+ }
297
+
273
298
  /*
274
299
  * Free a worksheet object.
275
300
  */
@@ -282,6 +307,7 @@ lxw_worksheet_free(lxw_worksheet *worksheet)
282
307
  lxw_merged_range *merged_range;
283
308
  lxw_image_options *image_options;
284
309
  lxw_selection *selection;
310
+ lxw_data_validation *data_validation;
285
311
  lxw_rel_tuple *relationship;
286
312
 
287
313
  if (!worksheet)
@@ -311,7 +337,6 @@ lxw_worksheet_free(lxw_worksheet *worksheet)
311
337
  }
312
338
 
313
339
  if (worksheet->hyperlinks) {
314
-
315
340
  for (row = RB_MIN(lxw_table_rows, worksheet->hyperlinks); row;
316
341
  row = next_row) {
317
342
 
@@ -363,6 +388,16 @@ lxw_worksheet_free(lxw_worksheet *worksheet)
363
388
  free(worksheet->selections);
364
389
  }
365
390
 
391
+ if (worksheet->data_validations) {
392
+ while (!STAILQ_EMPTY(worksheet->data_validations)) {
393
+ data_validation = STAILQ_FIRST(worksheet->data_validations);
394
+ STAILQ_REMOVE_HEAD(worksheet->data_validations, list_pointers);
395
+ _free_data_validation(data_validation);
396
+ }
397
+
398
+ free(worksheet->data_validations);
399
+ }
400
+
366
401
  /* TODO. Add function for freeing the relationship lists. */
367
402
  while (!STAILQ_EMPTY(worksheet->external_hyperlinks)) {
368
403
  relationship = STAILQ_FIRST(worksheet->external_hyperlinks);
@@ -862,6 +897,61 @@ lxw_basename(const char *path)
862
897
  return back_slash + 1;
863
898
  }
864
899
 
900
+ /* Function to count the total concatenated length of the strings in a
901
+ * validation list array, including commas. */
902
+ size_t
903
+ _validation_list_length(char **list)
904
+ {
905
+ uint8_t i = 0;
906
+ size_t length = 0;
907
+
908
+ if (!list || !list[0])
909
+ return 0;
910
+
911
+ while (list[i] && length <= LXW_VALIDATION_MAX_STRING_LENGTH) {
912
+ /* Include commas in the length. */
913
+ length += 1 + lxw_utf8_strlen(list[i]);
914
+ i++;
915
+ }
916
+
917
+ /* Adjust the count for extraneous comma at end. */
918
+ length--;
919
+
920
+ return length;
921
+ }
922
+
923
+ /* Function to convert an array of strings into a CSV string for data
924
+ * validation lists. */
925
+ char *
926
+ _validation_list_to_csv(char **list)
927
+ {
928
+ uint8_t i = 0;
929
+ char *str;
930
+
931
+ /* Create a buffer for the concatenated, and quoted, string. */
932
+ /* Add +3 for quotes and EOL. */
933
+ str = calloc(1, LXW_VALIDATION_MAX_STRING_LENGTH + 3);
934
+ if (!str)
935
+ return NULL;
936
+
937
+ /* Add the start quote and first element. */
938
+ strcat(str, "\"");
939
+ strcat(str, list[0]);
940
+
941
+ /* Add the other elements preceded by a comma. */
942
+ i = 1;
943
+ while (list[i]) {
944
+ strcat(str, ",");
945
+ strcat(str, list[i]);
946
+ i++;
947
+ }
948
+
949
+ /* Add the end quote. */
950
+ strcat(str, "\"");
951
+
952
+ return str;
953
+ }
954
+
865
955
  /*****************************************************************************
866
956
  *
867
957
  * XML functions.
@@ -2067,11 +2157,14 @@ _process_png(lxw_image_options *image_options)
2067
2157
  uint32_t height = 0;
2068
2158
  double x_dpi = 96;
2069
2159
  double y_dpi = 96;
2160
+ int fseek_err;
2070
2161
 
2071
2162
  FILE *stream = image_options->stream;
2072
2163
 
2073
2164
  /* Skip another 4 bytes to the end of the PNG header. */
2074
- fseek(stream, 4, SEEK_CUR);
2165
+ fseek_err = fseek(stream, 4, SEEK_CUR);
2166
+ if (fseek_err)
2167
+ goto file_error;
2075
2168
 
2076
2169
  while (!feof(stream)) {
2077
2170
 
@@ -2131,17 +2224,16 @@ _process_png(lxw_image_options *image_options)
2131
2224
  if (memcmp(type, "IEND", 4) == 0)
2132
2225
  break;
2133
2226
 
2134
- if (!feof(stream))
2135
- fseek(stream, offset, SEEK_CUR);
2227
+ if (!feof(stream)) {
2228
+ fseek_err = fseek(stream, offset, SEEK_CUR);
2229
+ if (fseek_err)
2230
+ goto file_error;
2231
+ }
2136
2232
  }
2137
2233
 
2138
2234
  /* Ensure that we read some valid data from the file. */
2139
- if (width == 0) {
2140
- LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
2141
- "no size data found in file: %s.",
2142
- image_options->filename);
2143
- return LXW_ERROR_IMAGE_DIMENSIONS;
2144
- }
2235
+ if (width == 0)
2236
+ goto file_error;
2145
2237
 
2146
2238
  /* Set the image metadata. */
2147
2239
  image_options->image_type = LXW_IMAGE_PNG;
@@ -2152,6 +2244,13 @@ _process_png(lxw_image_options *image_options)
2152
2244
  image_options->extension = lxw_strdup("png");
2153
2245
 
2154
2246
  return LXW_NO_ERROR;
2247
+
2248
+ file_error:
2249
+ LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
2250
+ "no size data found in file: %s.",
2251
+ image_options->filename);
2252
+
2253
+ return LXW_ERROR_IMAGE_DIMENSIONS;
2155
2254
  }
2156
2255
 
2157
2256
  /*
@@ -2167,11 +2266,14 @@ _process_jpeg(lxw_image_options *image_options)
2167
2266
  uint16_t height = 0;
2168
2267
  double x_dpi = 96;
2169
2268
  double y_dpi = 96;
2269
+ int fseek_err;
2170
2270
 
2171
2271
  FILE *stream = image_options->stream;
2172
2272
 
2173
2273
  /* Read back 2 bytes to the end of the initial 0xFFD8 marker. */
2174
- fseek(stream, -2, SEEK_CUR);
2274
+ fseek_err = fseek(stream, -2, SEEK_CUR);
2275
+ if (fseek_err)
2276
+ goto file_error;
2175
2277
 
2176
2278
  /* Search through the image data to read the height and width in the */
2177
2279
  /* 0xFFC0/C2 element. Also read the DPI in the 0xFFE0 element. */
@@ -2193,7 +2295,9 @@ _process_jpeg(lxw_image_options *image_options)
2193
2295
 
2194
2296
  if (marker == 0xFFC0 || marker == 0xFFC2) {
2195
2297
  /* Skip 1 byte to height and width. */
2196
- fseek(stream, 1, SEEK_CUR);
2298
+ fseek_err = fseek(stream, 1, SEEK_CUR);
2299
+ if (fseek_err)
2300
+ goto file_error;
2197
2301
 
2198
2302
  if (fread(&height, sizeof(height), 1, stream) < 1)
2199
2303
  break;
@@ -2212,7 +2316,9 @@ _process_jpeg(lxw_image_options *image_options)
2212
2316
  uint16_t y_density = 0;
2213
2317
  uint8_t units = 1;
2214
2318
 
2215
- fseek(stream, 7, SEEK_CUR);
2319
+ fseek_err = fseek(stream, 7, SEEK_CUR);
2320
+ if (fseek_err)
2321
+ goto file_error;
2216
2322
 
2217
2323
  if (fread(&units, sizeof(units), 1, stream) < 1)
2218
2324
  break;
@@ -2242,17 +2348,16 @@ _process_jpeg(lxw_image_options *image_options)
2242
2348
  if (marker == 0xFFDA)
2243
2349
  break;
2244
2350
 
2245
- if (!feof(stream))
2246
- fseek(stream, offset, SEEK_CUR);
2351
+ if (!feof(stream)) {
2352
+ fseek_err = fseek(stream, offset, SEEK_CUR);
2353
+ if (fseek_err)
2354
+ goto file_error;
2355
+ }
2247
2356
  }
2248
2357
 
2249
2358
  /* Ensure that we read some valid data from the file. */
2250
- if (width == 0) {
2251
- LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
2252
- "no size data found in file: %s.",
2253
- image_options->filename);
2254
- return LXW_ERROR_IMAGE_DIMENSIONS;
2255
- }
2359
+ if (width == 0)
2360
+ goto file_error;
2256
2361
 
2257
2362
  /* Set the image metadata. */
2258
2363
  image_options->image_type = LXW_IMAGE_JPEG;
@@ -2263,6 +2368,13 @@ _process_jpeg(lxw_image_options *image_options)
2263
2368
  image_options->extension = lxw_strdup("jpeg");
2264
2369
 
2265
2370
  return LXW_NO_ERROR;
2371
+
2372
+ file_error:
2373
+ LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
2374
+ "no size data found in file: %s.",
2375
+ image_options->filename);
2376
+
2377
+ return LXW_ERROR_IMAGE_DIMENSIONS;
2266
2378
  }
2267
2379
 
2268
2380
  /*
@@ -2275,11 +2387,14 @@ _process_bmp(lxw_image_options *image_options)
2275
2387
  uint32_t height = 0;
2276
2388
  double x_dpi = 96;
2277
2389
  double y_dpi = 96;
2390
+ int fseek_err;
2278
2391
 
2279
2392
  FILE *stream = image_options->stream;
2280
2393
 
2281
2394
  /* Skip another 14 bytes to the start of the BMP height/width. */
2282
- fseek(stream, 14, SEEK_CUR);
2395
+ fseek_err = fseek(stream, 14, SEEK_CUR);
2396
+ if (fseek_err)
2397
+ goto file_error;
2283
2398
 
2284
2399
  if (fread(&width, sizeof(width), 1, stream) < 1)
2285
2400
  width = 0;
@@ -2288,12 +2403,8 @@ _process_bmp(lxw_image_options *image_options)
2288
2403
  height = 0;
2289
2404
 
2290
2405
  /* Ensure that we read some valid data from the file. */
2291
- if (width == 0) {
2292
- LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
2293
- "no size data found in file: %s.",
2294
- image_options->filename);
2295
- return LXW_ERROR_IMAGE_DIMENSIONS;
2296
- }
2406
+ if (width == 0)
2407
+ goto file_error;
2297
2408
 
2298
2409
  /* Set the image metadata. */
2299
2410
  image_options->image_type = LXW_IMAGE_BMP;
@@ -2304,6 +2415,13 @@ _process_bmp(lxw_image_options *image_options)
2304
2415
  image_options->extension = lxw_strdup("bmp");
2305
2416
 
2306
2417
  return LXW_NO_ERROR;
2418
+
2419
+ file_error:
2420
+ LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
2421
+ "no size data found in file: %s.",
2422
+ image_options->filename);
2423
+
2424
+ return LXW_ERROR_IMAGE_DIMENSIONS;
2307
2425
  }
2308
2426
 
2309
2427
  /*
@@ -3286,6 +3404,224 @@ _write_drawings(lxw_worksheet *self)
3286
3404
  _write_drawing(self, self->rel_count);
3287
3405
  }
3288
3406
 
3407
+ /*
3408
+ * Write the <formula1> element for numbers.
3409
+ */
3410
+ STATIC void
3411
+ _worksheet_write_formula1_num(lxw_worksheet *self, double number)
3412
+ {
3413
+ char data[LXW_ATTR_32];
3414
+
3415
+ lxw_snprintf(data, LXW_ATTR_32, "%.16g", number);
3416
+
3417
+ lxw_xml_data_element(self->file, "formula1", data, NULL);
3418
+ }
3419
+
3420
+ /*
3421
+ * Write the <formula1> element for strings/formulas.
3422
+ */
3423
+ STATIC void
3424
+ _worksheet_write_formula1_str(lxw_worksheet *self, char *str)
3425
+ {
3426
+ lxw_xml_data_element(self->file, "formula1", str, NULL);
3427
+ }
3428
+
3429
+ /*
3430
+ * Write the <formula2> element for numbers.
3431
+ */
3432
+ STATIC void
3433
+ _worksheet_write_formula2_num(lxw_worksheet *self, double number)
3434
+ {
3435
+ char data[LXW_ATTR_32];
3436
+
3437
+ lxw_snprintf(data, LXW_ATTR_32, "%.16g", number);
3438
+
3439
+ lxw_xml_data_element(self->file, "formula2", data, NULL);
3440
+ }
3441
+
3442
+ /*
3443
+ * Write the <formula2> element for strings/formulas.
3444
+ */
3445
+ STATIC void
3446
+ _worksheet_write_formula2_str(lxw_worksheet *self, char *str)
3447
+ {
3448
+ lxw_xml_data_element(self->file, "formula2", str, NULL);
3449
+ }
3450
+
3451
+ /*
3452
+ * Write the <dataValidation> element.
3453
+ */
3454
+ STATIC void
3455
+ _worksheet_write_data_validation(lxw_worksheet *self,
3456
+ lxw_data_validation *validation)
3457
+ {
3458
+ struct xml_attribute_list attributes;
3459
+ struct xml_attribute *attribute;
3460
+ uint8_t is_between = 0;
3461
+
3462
+ LXW_INIT_ATTRIBUTES();
3463
+
3464
+ switch (validation->validate) {
3465
+ case LXW_VALIDATION_TYPE_INTEGER:
3466
+ case LXW_VALIDATION_TYPE_INTEGER_FORMULA:
3467
+ LXW_PUSH_ATTRIBUTES_STR("type", "whole");
3468
+ break;
3469
+ case LXW_VALIDATION_TYPE_DECIMAL:
3470
+ case LXW_VALIDATION_TYPE_DECIMAL_FORMULA:
3471
+ LXW_PUSH_ATTRIBUTES_STR("type", "decimal");
3472
+ break;
3473
+ case LXW_VALIDATION_TYPE_LIST:
3474
+ case LXW_VALIDATION_TYPE_LIST_FORMULA:
3475
+ LXW_PUSH_ATTRIBUTES_STR("type", "list");
3476
+ break;
3477
+ case LXW_VALIDATION_TYPE_DATE:
3478
+ case LXW_VALIDATION_TYPE_DATE_FORMULA:
3479
+ case LXW_VALIDATION_TYPE_DATE_NUMBER:
3480
+ LXW_PUSH_ATTRIBUTES_STR("type", "date");
3481
+ break;
3482
+ case LXW_VALIDATION_TYPE_TIME:
3483
+ case LXW_VALIDATION_TYPE_TIME_FORMULA:
3484
+ case LXW_VALIDATION_TYPE_TIME_NUMBER:
3485
+ LXW_PUSH_ATTRIBUTES_STR("type", "time");
3486
+ break;
3487
+ case LXW_VALIDATION_TYPE_LENGTH:
3488
+ case LXW_VALIDATION_TYPE_LENGTH_FORMULA:
3489
+ LXW_PUSH_ATTRIBUTES_STR("type", "textLength");
3490
+ break;
3491
+ case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
3492
+ LXW_PUSH_ATTRIBUTES_STR("type", "custom");
3493
+ break;
3494
+ }
3495
+
3496
+ switch (validation->criteria) {
3497
+ case LXW_VALIDATION_CRITERIA_EQUAL_TO:
3498
+ LXW_PUSH_ATTRIBUTES_STR("operator", "equal");
3499
+ break;
3500
+ case LXW_VALIDATION_CRITERIA_NOT_EQUAL_TO:
3501
+ LXW_PUSH_ATTRIBUTES_STR("operator", "notEqual");
3502
+ break;
3503
+ case LXW_VALIDATION_CRITERIA_LESS_THAN:
3504
+ LXW_PUSH_ATTRIBUTES_STR("operator", "lessThan");
3505
+ break;
3506
+ case LXW_VALIDATION_CRITERIA_LESS_THAN_OR_EQUAL_TO:
3507
+ LXW_PUSH_ATTRIBUTES_STR("operator", "lessThanOrEqual");
3508
+ break;
3509
+ case LXW_VALIDATION_CRITERIA_GREATER_THAN:
3510
+ LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThan");
3511
+ break;
3512
+ case LXW_VALIDATION_CRITERIA_GREATER_THAN_OR_EQUAL_TO:
3513
+ LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThanOrEqual");
3514
+ break;
3515
+ case LXW_VALIDATION_CRITERIA_BETWEEN:
3516
+ /* Between is the default for 2 formulas and isn't added. */
3517
+ is_between = 1;
3518
+ break;
3519
+ case LXW_VALIDATION_CRITERIA_NOT_BETWEEN:
3520
+ is_between = 1;
3521
+ LXW_PUSH_ATTRIBUTES_STR("operator", "notBetween");
3522
+ break;
3523
+ }
3524
+
3525
+ if (validation->error_type == LXW_VALIDATION_ERROR_TYPE_WARNING)
3526
+ LXW_PUSH_ATTRIBUTES_STR("errorStyle", "warning");
3527
+
3528
+ if (validation->error_type == LXW_VALIDATION_ERROR_TYPE_INFORMATION)
3529
+ LXW_PUSH_ATTRIBUTES_STR("errorStyle", "information");
3530
+
3531
+ if (validation->ignore_blank)
3532
+ LXW_PUSH_ATTRIBUTES_INT("allowBlank", 1);
3533
+
3534
+ if (validation->dropdown == LXW_VALIDATION_OFF)
3535
+ LXW_PUSH_ATTRIBUTES_INT("showDropDown", 1);
3536
+
3537
+ if (validation->show_input)
3538
+ LXW_PUSH_ATTRIBUTES_INT("showInputMessage", 1);
3539
+
3540
+ if (validation->show_error)
3541
+ LXW_PUSH_ATTRIBUTES_INT("showErrorMessage", 1);
3542
+
3543
+ if (validation->error_title)
3544
+ LXW_PUSH_ATTRIBUTES_STR("errorTitle", validation->error_title);
3545
+
3546
+ if (validation->error_message)
3547
+ LXW_PUSH_ATTRIBUTES_STR("error", validation->error_message);
3548
+
3549
+ if (validation->input_title)
3550
+ LXW_PUSH_ATTRIBUTES_STR("promptTitle", validation->input_title);
3551
+
3552
+ if (validation->input_message)
3553
+ LXW_PUSH_ATTRIBUTES_STR("prompt", validation->input_message);
3554
+
3555
+ LXW_PUSH_ATTRIBUTES_STR("sqref", validation->sqref);
3556
+
3557
+ if (validation->validate == LXW_VALIDATION_TYPE_ANY)
3558
+ lxw_xml_empty_tag(self->file, "dataValidation", &attributes);
3559
+ else
3560
+ lxw_xml_start_tag(self->file, "dataValidation", &attributes);
3561
+
3562
+ /* Write the formula1 and formula2 elements. */
3563
+ switch (validation->validate) {
3564
+ case LXW_VALIDATION_TYPE_INTEGER:
3565
+ case LXW_VALIDATION_TYPE_DECIMAL:
3566
+ case LXW_VALIDATION_TYPE_LENGTH:
3567
+ case LXW_VALIDATION_TYPE_DATE:
3568
+ case LXW_VALIDATION_TYPE_TIME:
3569
+ case LXW_VALIDATION_TYPE_DATE_NUMBER:
3570
+ case LXW_VALIDATION_TYPE_TIME_NUMBER:
3571
+ _worksheet_write_formula1_num(self, validation->value_number);
3572
+ if (is_between)
3573
+ _worksheet_write_formula2_num(self,
3574
+ validation->maximum_number);
3575
+ break;
3576
+ case LXW_VALIDATION_TYPE_INTEGER_FORMULA:
3577
+ case LXW_VALIDATION_TYPE_DECIMAL_FORMULA:
3578
+ case LXW_VALIDATION_TYPE_LENGTH_FORMULA:
3579
+ case LXW_VALIDATION_TYPE_DATE_FORMULA:
3580
+ case LXW_VALIDATION_TYPE_TIME_FORMULA:
3581
+ case LXW_VALIDATION_TYPE_LIST:
3582
+ case LXW_VALIDATION_TYPE_LIST_FORMULA:
3583
+ case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
3584
+ _worksheet_write_formula1_str(self, validation->value_formula);
3585
+ if (is_between)
3586
+ _worksheet_write_formula2_str(self,
3587
+ validation->maximum_formula);
3588
+ break;
3589
+ }
3590
+
3591
+ if (validation->validate != LXW_VALIDATION_TYPE_ANY)
3592
+ lxw_xml_end_tag(self->file, "dataValidation");
3593
+
3594
+ LXW_FREE_ATTRIBUTES();
3595
+ }
3596
+
3597
+ /*
3598
+ * Write the <dataValidations> element.
3599
+ */
3600
+ STATIC void
3601
+ _worksheet_write_data_validations(lxw_worksheet *self)
3602
+ {
3603
+ struct xml_attribute_list attributes;
3604
+ struct xml_attribute *attribute;
3605
+ lxw_data_validation *data_validation;
3606
+
3607
+ if (self->num_validations == 0)
3608
+ return;
3609
+
3610
+ LXW_INIT_ATTRIBUTES();
3611
+ LXW_PUSH_ATTRIBUTES_INT("count", self->num_validations);
3612
+
3613
+ lxw_xml_start_tag(self->file, "dataValidations", &attributes);
3614
+
3615
+ STAILQ_FOREACH(data_validation, self->data_validations, list_pointers) {
3616
+ /* Write the dataValidation element. */
3617
+ _worksheet_write_data_validation(self, data_validation);
3618
+ }
3619
+
3620
+ lxw_xml_end_tag(self->file, "dataValidations");
3621
+
3622
+ LXW_FREE_ATTRIBUTES();
3623
+ }
3624
+
3289
3625
  /*
3290
3626
  * Assemble and write the XML file.
3291
3627
  */
@@ -3328,6 +3664,9 @@ lxw_worksheet_assemble_xml_file(lxw_worksheet *self)
3328
3664
  /* Write the mergeCells element. */
3329
3665
  _worksheet_write_merge_cells(self);
3330
3666
 
3667
+ /* Write the dataValidations element. */
3668
+ _worksheet_write_data_validations(self);
3669
+
3331
3670
  /* Write the hyperlink element. */
3332
3671
  _worksheet_write_hyperlinks(self);
3333
3672
 
@@ -3405,7 +3744,7 @@ worksheet_write_string(lxw_worksheet *self,
3405
3744
  if (format)
3406
3745
  return worksheet_write_blank(self, row_num, col_num, format);
3407
3746
  else
3408
- return LXW_ERROR_NULL_PARAMETER_IGNORED;
3747
+ return LXW_NO_ERROR;
3409
3748
  }
3410
3749
 
3411
3750
  err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
@@ -3533,7 +3872,7 @@ worksheet_write_array_formula_num(lxw_worksheet *self,
3533
3872
 
3534
3873
  /* Define the array range. */
3535
3874
  range = calloc(1, LXW_MAX_CELL_RANGE_LENGTH);
3536
- RETURN_ON_MEM_ERROR(range, -1);
3875
+ RETURN_ON_MEM_ERROR(range, LXW_ERROR_MEMORY_MALLOC_FAILED);
3537
3876
 
3538
3877
  if (first_row == last_row && first_col == last_col)
3539
3878
  lxw_rowcol_to_cell(range, first_row, last_col);
@@ -4119,7 +4458,7 @@ worksheet_merge_range(lxw_worksheet *self, lxw_row_t first_row,
4119
4458
 
4120
4459
  /* Store the merge range. */
4121
4460
  merged_range = calloc(1, sizeof(lxw_merged_range));
4122
- RETURN_ON_MEM_ERROR(merged_range, 2);
4461
+ RETURN_ON_MEM_ERROR(merged_range, LXW_ERROR_MEMORY_MALLOC_FAILED);
4123
4462
 
4124
4463
  merged_range->first_row = first_row;
4125
4464
  merged_range->first_col = first_col;
@@ -4861,12 +5200,16 @@ worksheet_insert_image_opt(lxw_worksheet *self,
4861
5200
  if (!short_name) {
4862
5201
  LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
4863
5202
  "couldn't get basename for file: %s.", filename);
5203
+ fclose(image_stream);
4864
5204
  return LXW_ERROR_PARAMETER_VALIDATION;
4865
5205
  }
4866
5206
 
4867
5207
  /* Create a new object to hold the image options. */
4868
5208
  options = calloc(1, sizeof(lxw_image_options));
4869
- RETURN_ON_MEM_ERROR(options, LXW_ERROR_MEMORY_MALLOC_FAILED);
5209
+ if (!options) {
5210
+ fclose(image_stream);
5211
+ return LXW_ERROR_MEMORY_MALLOC_FAILED;
5212
+ }
4870
5213
 
4871
5214
  if (user_options) {
4872
5215
  memcpy(options, user_options, sizeof(lxw_image_options));
@@ -4889,10 +5232,12 @@ worksheet_insert_image_opt(lxw_worksheet *self,
4889
5232
 
4890
5233
  if (_get_image_properties(options) == LXW_NO_ERROR) {
4891
5234
  STAILQ_INSERT_TAIL(self->image_data, options, list_pointers);
5235
+ fclose(image_stream);
4892
5236
  return LXW_NO_ERROR;
4893
5237
  }
4894
5238
  else {
4895
5239
  free(options);
5240
+ fclose(image_stream);
4896
5241
  return LXW_ERROR_IMAGE_DIMENSIONS;
4897
5242
  }
4898
5243
  }
@@ -4990,3 +5335,288 @@ worksheet_insert_chart(lxw_worksheet *self,
4990
5335
  {
4991
5336
  return worksheet_insert_chart_opt(self, row_num, col_num, chart, NULL);
4992
5337
  }
5338
+
5339
+ /*
5340
+ * Add a data validation to a worksheet, for a range. Ironically this requires
5341
+ * a lot of validation of the user input.
5342
+ */
5343
+ lxw_error
5344
+ worksheet_data_validation_range(lxw_worksheet *self, lxw_row_t first_row,
5345
+ lxw_col_t first_col,
5346
+ lxw_row_t last_row,
5347
+ lxw_col_t last_col,
5348
+ lxw_data_validation *validation)
5349
+ {
5350
+ lxw_data_validation *copy;
5351
+ uint8_t is_between = LXW_FALSE;
5352
+ uint8_t is_formula = LXW_FALSE;
5353
+ uint8_t has_criteria = LXW_TRUE;
5354
+ lxw_error err;
5355
+ lxw_row_t tmp_row;
5356
+ lxw_col_t tmp_col;
5357
+ size_t length;
5358
+
5359
+ /* No action is required for validation type 'any' unless there are
5360
+ * input messages to display.*/
5361
+ if (validation->validate == LXW_VALIDATION_TYPE_ANY
5362
+ && !(validation->input_title || validation->input_message)) {
5363
+
5364
+ return LXW_NO_ERROR;
5365
+ }
5366
+
5367
+ /* Check for formula types. */
5368
+ switch (validation->validate) {
5369
+ case LXW_VALIDATION_TYPE_INTEGER_FORMULA:
5370
+ case LXW_VALIDATION_TYPE_DECIMAL_FORMULA:
5371
+ case LXW_VALIDATION_TYPE_LIST_FORMULA:
5372
+ case LXW_VALIDATION_TYPE_LENGTH_FORMULA:
5373
+ case LXW_VALIDATION_TYPE_DATE_FORMULA:
5374
+ case LXW_VALIDATION_TYPE_TIME_FORMULA:
5375
+ case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
5376
+ is_formula = LXW_TRUE;
5377
+ break;
5378
+ }
5379
+
5380
+ /* Check for types without a criteria. */
5381
+ switch (validation->validate) {
5382
+ case LXW_VALIDATION_TYPE_LIST:
5383
+ case LXW_VALIDATION_TYPE_LIST_FORMULA:
5384
+ case LXW_VALIDATION_TYPE_ANY:
5385
+ case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
5386
+ has_criteria = LXW_FALSE;
5387
+ break;
5388
+ }
5389
+
5390
+ /* Check that a validation parameter has been specified
5391
+ * except for 'list', 'any' and 'custom'. */
5392
+ if (has_criteria && validation->criteria == LXW_VALIDATION_CRITERIA_NONE) {
5393
+
5394
+ LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
5395
+ "criteria parameter must be specified.");
5396
+ return LXW_ERROR_PARAMETER_VALIDATION;
5397
+ }
5398
+
5399
+ /* Check for "between" criteria so we can do additional checks. */
5400
+ if (has_criteria
5401
+ && (validation->criteria == LXW_VALIDATION_CRITERIA_BETWEEN
5402
+ || validation->criteria == LXW_VALIDATION_CRITERIA_NOT_BETWEEN)) {
5403
+
5404
+ is_between = LXW_TRUE;
5405
+ }
5406
+
5407
+ /* Check that formula values are non NULL. */
5408
+ if (is_formula) {
5409
+ if (is_between) {
5410
+ if (!validation->minimum_formula) {
5411
+ LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
5412
+ "minimum_formula parameter cannot be NULL.");
5413
+ return LXW_ERROR_PARAMETER_VALIDATION;
5414
+ }
5415
+ if (!validation->maximum_formula) {
5416
+ LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
5417
+ "maximum_formula parameter cannot be NULL.");
5418
+ return LXW_ERROR_PARAMETER_VALIDATION;
5419
+ }
5420
+ }
5421
+ else {
5422
+ if (!validation->value_formula) {
5423
+ LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
5424
+ "formula parameter cannot be NULL.");
5425
+ return LXW_ERROR_PARAMETER_VALIDATION;
5426
+ }
5427
+ }
5428
+ }
5429
+
5430
+ /* Check Excel limitations on input strings. */
5431
+ if (validation->input_title) {
5432
+ length = lxw_utf8_strlen(validation->input_title);
5433
+ if (length > LXW_VALIDATION_MAX_TITLE_LENGTH) {
5434
+ LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
5435
+ "input_title length > Excel limit of %d.",
5436
+ LXW_VALIDATION_MAX_TITLE_LENGTH);
5437
+ return LXW_ERROR_32_STRING_LENGTH_EXCEEDED;
5438
+ }
5439
+ }
5440
+
5441
+ if (validation->error_title) {
5442
+ length = lxw_utf8_strlen(validation->error_title);
5443
+ if (length > LXW_VALIDATION_MAX_TITLE_LENGTH) {
5444
+ LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
5445
+ "error_title length > Excel limit of %d.",
5446
+ LXW_VALIDATION_MAX_TITLE_LENGTH);
5447
+ return LXW_ERROR_32_STRING_LENGTH_EXCEEDED;
5448
+ }
5449
+ }
5450
+
5451
+ if (validation->input_message) {
5452
+ length = lxw_utf8_strlen(validation->input_message);
5453
+ if (length > LXW_VALIDATION_MAX_STRING_LENGTH) {
5454
+ LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
5455
+ "input_message length > Excel limit of %d.",
5456
+ LXW_VALIDATION_MAX_STRING_LENGTH);
5457
+ return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
5458
+ }
5459
+ }
5460
+
5461
+ if (validation->error_message) {
5462
+ length = lxw_utf8_strlen(validation->error_message);
5463
+ if (length > LXW_VALIDATION_MAX_STRING_LENGTH) {
5464
+ LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
5465
+ "error_message length > Excel limit of %d.",
5466
+ LXW_VALIDATION_MAX_STRING_LENGTH);
5467
+ return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
5468
+ }
5469
+ }
5470
+
5471
+ if (validation->validate == LXW_VALIDATION_TYPE_LIST) {
5472
+ length = _validation_list_length(validation->value_list);
5473
+
5474
+ if (length == 0) {
5475
+ LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
5476
+ "list parameters cannot be zero.");
5477
+ return LXW_ERROR_PARAMETER_VALIDATION;
5478
+ }
5479
+
5480
+ if (length > LXW_VALIDATION_MAX_STRING_LENGTH) {
5481
+ LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
5482
+ "list length with commas > Excel limit of %d.",
5483
+ LXW_VALIDATION_MAX_STRING_LENGTH);
5484
+ return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
5485
+ }
5486
+ }
5487
+
5488
+ /* Swap last row/col with first row/col as necessary */
5489
+ if (first_row > last_row) {
5490
+ tmp_row = last_row;
5491
+ last_row = first_row;
5492
+ first_row = tmp_row;
5493
+ }
5494
+ if (first_col > last_col) {
5495
+ tmp_col = last_col;
5496
+ last_col = first_col;
5497
+ first_col = tmp_col;
5498
+ }
5499
+
5500
+ /* Check that dimensions are valid but don't store them. */
5501
+ err = _check_dimensions(self, last_row, last_col, LXW_TRUE, LXW_TRUE);
5502
+ if (err)
5503
+ return err;
5504
+
5505
+ /* Create a copy of the parameters from the user data validation. */
5506
+ copy = calloc(1, sizeof(lxw_data_validation));
5507
+ GOTO_LABEL_ON_MEM_ERROR(copy, mem_error);
5508
+
5509
+ /* Create the data validation range. */
5510
+ if (first_row == last_row && first_col == last_col)
5511
+ lxw_rowcol_to_cell(copy->sqref, first_row, last_col);
5512
+ else
5513
+ lxw_rowcol_to_range(copy->sqref, first_row, first_col, last_row,
5514
+ last_col);
5515
+
5516
+ /* Copy the parameters from the user data validation. */
5517
+ copy->validate = validation->validate;
5518
+ copy->value_number = validation->value_number;
5519
+ copy->error_type = validation->error_type;
5520
+ copy->dropdown = validation->dropdown;
5521
+ copy->is_between = is_between;
5522
+
5523
+ if (has_criteria)
5524
+ copy->criteria = validation->criteria;
5525
+
5526
+ if (is_between) {
5527
+ copy->value_number = validation->minimum_number;
5528
+ copy->maximum_number = validation->maximum_number;
5529
+ }
5530
+
5531
+ /* Copy the input/error titles and messages. */
5532
+ if (validation->input_title) {
5533
+ copy->input_title = lxw_strdup_formula(validation->input_title);
5534
+ GOTO_LABEL_ON_MEM_ERROR(copy->input_title, mem_error);
5535
+ }
5536
+
5537
+ if (validation->input_message) {
5538
+ copy->input_message = lxw_strdup_formula(validation->input_message);
5539
+ GOTO_LABEL_ON_MEM_ERROR(copy->input_message, mem_error);
5540
+ }
5541
+
5542
+ if (validation->error_title) {
5543
+ copy->error_title = lxw_strdup_formula(validation->error_title);
5544
+ GOTO_LABEL_ON_MEM_ERROR(copy->error_title, mem_error);
5545
+ }
5546
+
5547
+ if (validation->error_message) {
5548
+ copy->error_message = lxw_strdup_formula(validation->error_message);
5549
+ GOTO_LABEL_ON_MEM_ERROR(copy->error_message, mem_error);
5550
+ }
5551
+
5552
+ /* Copy the formula strings. */
5553
+ if (is_formula) {
5554
+ if (is_between) {
5555
+ copy->value_formula =
5556
+ lxw_strdup_formula(validation->minimum_formula);
5557
+ GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
5558
+ copy->maximum_formula =
5559
+ lxw_strdup_formula(validation->maximum_formula);
5560
+ GOTO_LABEL_ON_MEM_ERROR(copy->maximum_formula, mem_error);
5561
+ }
5562
+ else {
5563
+ copy->value_formula =
5564
+ lxw_strdup_formula(validation->value_formula);
5565
+ GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
5566
+ }
5567
+ }
5568
+
5569
+ /* Copy the validation list as a csv string. */
5570
+ if (validation->validate == LXW_VALIDATION_TYPE_LIST) {
5571
+ copy->value_formula = _validation_list_to_csv(validation->value_list);
5572
+ GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
5573
+ }
5574
+
5575
+ if (validation->validate == LXW_VALIDATION_TYPE_LIST_FORMULA) {
5576
+ copy->value_formula = lxw_strdup_formula(validation->value_formula);
5577
+ GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
5578
+ }
5579
+
5580
+ if (validation->validate == LXW_VALIDATION_TYPE_DATE
5581
+ || validation->validate == LXW_VALIDATION_TYPE_TIME) {
5582
+ if (is_between) {
5583
+ copy->value_number =
5584
+ lxw_datetime_to_excel_date(&validation->minimum_datetime,
5585
+ LXW_EPOCH_1900);
5586
+ copy->maximum_number =
5587
+ lxw_datetime_to_excel_date(&validation->maximum_datetime,
5588
+ LXW_EPOCH_1900);
5589
+ }
5590
+ else {
5591
+ copy->value_number =
5592
+ lxw_datetime_to_excel_date(&validation->value_datetime,
5593
+ LXW_EPOCH_1900);
5594
+ }
5595
+ }
5596
+
5597
+ /* These options are on by default so we can't take plain booleans. */
5598
+ copy->ignore_blank = validation->ignore_blank ^ 1;
5599
+ copy->show_input = validation->show_input ^ 1;
5600
+ copy->show_error = validation->show_error ^ 1;
5601
+
5602
+ STAILQ_INSERT_TAIL(self->data_validations, copy, list_pointers);
5603
+
5604
+ self->num_validations++;
5605
+
5606
+ return LXW_NO_ERROR;
5607
+
5608
+ mem_error:
5609
+ _free_data_validation(copy);
5610
+ return LXW_ERROR_MEMORY_MALLOC_FAILED;
5611
+ }
5612
+
5613
+ /*
5614
+ * Add a data validation to a worksheet, for a cell.
5615
+ */
5616
+ lxw_error
5617
+ worksheet_data_validation_cell(lxw_worksheet *self, lxw_row_t row,
5618
+ lxw_col_t col, lxw_data_validation *validation)
5619
+ {
5620
+ return worksheet_data_validation_range(self, row, col,
5621
+ row, col, validation);
5622
+ }