7c2d61d2a9b9895747e83348a2165dd16e7ab49e
[scilab.git] / scilab / modules / spreadsheet / src / c / csvRead.c
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2010-2011 - DIGITEO - Allan CORNET
4  *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  *
14  */
15 #include <string.h>
16 #include <stdio.h>
17 #include "csvRead.h"
18 #include "sci_malloc.h"
19 #include "freeArrayOfString.h"
20 #include "mopen.h"
21 #include "mgetl.h"
22 #include "localization.h"
23 #include "expandPathVariable.h"
24 #include "FileExist.h"
25 #include "mclose.h"
26 #include "configvariable_interface.h"
27 #include "pcre_private.h"
28 #include "sciprint.h"
29 #include "splitLine.h"
30 #include "os_string.h"
31 #include "csvDefault.h"
32 #include "strsubst.h"
33 #include "Sciwarning.h"
34 // =============================================================================
35 #if _MSC_VER
36 #define READ_ONLY_TEXT_MODE "rt"
37 #else
38 #define READ_ONLY_TEXT_MODE "r"
39 #endif
40 // =============================================================================
41 static int getNumbersOfColumnsInLines(const char **lines, int sizelines,
42                                       const char *separator);
43 static int getNumbersOfColumnsInLine(const char *line, const char *separator);
44 static char **getStringsFromLines(const char **lines, int sizelines,
45                                   const char *separator, const char *decimal,
46                                   int m, int n);
47 static char **removeEmptyLinesAtTheEnd(const char **lines, int *sizelines);
48 static char *stripCharacters(const char *line);
49 static char **replaceStrings(const char **lines, int nbLines, const char **toreplace, int sizetoreplace);
50 static char **extractComments(const char **lines, int nbLines, const char *regexpcomments, int *nbcomments, int *iErr);
51 static char **removeComments(const char **lines, int nbLines, const char *regexpcomments, int *nbNewLine, int *iErr);
52 static char **removeAllBlankLines(const char **lines, int *sizelines);
53 // =============================================================================
54 csvResult* csvRead(const char *filename, const char *separator, const char *decimal, const char **toreplace, int sizetoreplace, const char *regexpcomments, int header)
55 {
56     wchar_t *expandedFilename = NULL;
57     wchar_t *wideFilename = NULL;
58     csvResult *result = NULL;
59     int fd = 0;
60     int f_swap = 0;
61     double res = 0.0;
62     int errMOPEN = MOPEN_INVALID_STATUS;
63     wchar_t **pwstLines = NULL;
64     char **pstLines = NULL;
65     int nbLines = 0;
66     char **replacedInLines = NULL;
67     char **pComments = NULL;
68     int nbComments = 0;
69
70     if ((filename == NULL) || (separator == NULL) || (decimal == NULL))
71     {
72         return NULL;
73     }
74
75     wideFilename = to_wide_string((char*)filename);
76     expandedFilename = expandPathVariableW(wideFilename);
77     FREE(wideFilename);
78
79     if (!FileExistW(expandedFilename))
80     {
81         result = (csvResult*)(MALLOC(sizeof(csvResult)));
82         if (result)
83         {
84             result->err = CSV_READ_FILE_NOT_EXIST;
85             result->m = 0;
86             result->n = 0;
87             result->pstrValues = NULL;
88             result->pstrComments = NULL;
89             result->nbComments = 0;
90         }
91
92         FREE(expandedFilename);
93         return result;
94     }
95
96     errMOPEN = mopen(expandedFilename, L"rt", f_swap, &fd); // rt = read only
97     if (expandedFilename)
98     {
99         FREE(expandedFilename);
100         expandedFilename = NULL;
101     }
102
103     if (errMOPEN != MOPEN_NO_ERROR)
104     {
105         result = (csvResult*)(MALLOC(sizeof(csvResult)));
106         if (result)
107         {
108             result->err = CSV_READ_MOPEN_ERROR;
109             result->m = 0;
110             result->n = 0;
111             result->pstrValues = NULL;
112             result->pstrComments = NULL;
113             result->nbComments = 0;
114
115         }
116         return result;
117     }
118
119     if (header != 0)
120     {
121         wchar_t **pwstHeaderLines = NULL;
122         mgetl(fd, header, &pwstHeaderLines);
123         FREE(pwstHeaderLines);
124     }
125
126     nbLines = mgetl(fd, -1, &pwstLines);
127     mclose(fd);
128
129     if (nbLines >= 0)
130     {
131         int i = 0;
132         pstLines = (char**)MALLOC(sizeof(char*) * nbLines);
133         for (i = 0 ; i < nbLines ; i++)
134         {
135             pstLines[i] = wide_string_to_UTF8(pwstLines[i]);
136         }
137         freeArrayOfWideString(pwstLines, nbLines);
138         pwstLines = NULL;
139     }
140     else
141     {
142         result = (csvResult*)(MALLOC(sizeof(csvResult)));
143         if (result)
144         {
145             result->err = CSV_READ_READLINES_ERROR;
146             result->m = 0;
147             result->n = 0;
148             result->pstrValues = NULL;
149             result->pstrComments = NULL;
150             result->nbComments = 0;
151         }
152         return result;
153     }
154
155     if (regexpcomments)
156     {
157         int iErr = 0;
158
159         pComments = extractComments((const char**)pstLines, nbLines, regexpcomments, &nbComments, &iErr);
160
161         if ((iErr == CAN_NOT_COMPILE_PATTERN) || (iErr == DELIMITER_NOT_ALPHANUMERIC))
162         {
163             result = (csvResult*)(MALLOC(sizeof(csvResult)));
164             if (result)
165             {
166                 if ((iErr == CAN_NOT_COMPILE_PATTERN) || (iErr == DELIMITER_NOT_ALPHANUMERIC))
167                 {
168                     iErr = CSV_READ_REGEXP_ERROR;
169                 }
170                 result->err = (csvReadError)iErr;
171                 result->m = 0;
172                 result->n = 0;
173                 result->pstrValues = NULL;
174                 result->pstrComments = NULL;
175                 result->nbComments = 0;
176             }
177             freeArrayOfString(pstLines, nbLines);
178             return result;
179         }
180
181         if (pComments)
182         {
183             char **pCleanedLines = NULL;
184             int nbCleanedLines = 0;
185             int i = 0;
186
187             pCleanedLines = removeComments((const char**)pstLines, nbLines, (const char*)regexpcomments, &nbCleanedLines, &iErr);
188             if (pCleanedLines)
189             {
190                 FREE(pstLines);
191                 pstLines = pCleanedLines;
192                 nbLines = nbCleanedLines;
193             }
194
195         }
196     }
197
198     if (toreplace && (sizetoreplace > 0))
199     {
200         replacedInLines = replaceStrings((const char**)pstLines, nbLines, toreplace, sizetoreplace);
201         if (replacedInLines)
202         {
203             freeArrayOfString(pstLines, nbLines);
204             pstLines = replacedInLines;
205         }
206     }
207
208     result = csvTextScan((const char**)pstLines, nbLines, (const char*)separator, (const char*)decimal);
209     freeArrayOfString(pstLines, nbLines);
210     freeArrayOfWideString(pwstLines, nbLines);
211
212     if (result)
213     {
214         result->pstrComments = pComments;
215         result->nbComments = nbComments;
216     }
217     else
218     {
219         freeArrayOfString(pComments, nbComments);
220     }
221
222     return result;
223 }
224 // =============================================================================
225 csvResult* csvTextScan(const char **lines, int numberOfLines, const char *separator, const char *decimal)
226 {
227     csvResult *result = NULL;
228     int nbRows = 0;
229     int nbColumns = 0;
230     char **cellsStrings = NULL;
231     char **cleanedLines = NULL;
232     int nbLines = numberOfLines;
233
234     if (strcmp(separator, decimal) == 0)
235     {
236         result = (csvResult*)(MALLOC(sizeof(csvResult)));
237         if (result)
238         {
239             result->err = CSV_READ_SEPARATOR_DECIMAL_EQUAL;
240             result->m = 0;
241             result->n = 0;
242             result->pstrValues = NULL;
243             result->pstrComments = NULL;
244             result->nbComments = 0;
245         }
246         return result;
247     }
248
249     // ticket 472
250     {
251         const char *blankMode = getCsvDefaultCsvIgnoreBlankLine();
252         if (strcmp(blankMode, "on") == 0)
253         {
254             char **tmpLines = removeAllBlankLines(lines, &nbLines);
255             if (tmpLines)
256             {
257                 freeArrayOfString(cleanedLines, nbLines);
258                 cleanedLines = tmpLines;
259             }
260         }
261         else
262         {
263             /* remove last lines empty (bug 7003 in scilab)*/
264             cleanedLines = removeEmptyLinesAtTheEnd(lines, &nbLines);
265         }
266     }
267
268     nbColumns = getNumbersOfColumnsInLines((const char **)cleanedLines, nbLines, separator);
269     if (nbColumns == 0)
270     {
271         result = (csvResult*)(MALLOC(sizeof(csvResult)));
272         if (result)
273         {
274             result->err = CSV_READ_COLUMNS_ERROR;
275             result->m = 0;
276             result->n = 0;
277             result->pstrValues = NULL;
278             result->pstrComments = NULL;
279             result->nbComments = 0;
280         }
281         FREE(cleanedLines);
282         return result;
283     }
284     else
285     {
286         nbRows = nbLines;
287     }
288
289     cellsStrings = getStringsFromLines((const char **)cleanedLines, nbLines, separator, decimal, nbColumns, nbRows);
290     if (cleanedLines)
291     {
292         freeArrayOfString(cleanedLines, nbLines);
293         cleanedLines = NULL;
294     }
295
296     if (cellsStrings)
297     {
298         result = (csvResult*)(MALLOC(sizeof(csvResult)));
299         if (result)
300         {
301             result->err = CSV_READ_NO_ERROR;
302             result->m = nbRows;
303             result->n = nbColumns;
304             result->pstrValues = cellsStrings;
305             result->pstrComments = NULL;
306             result->nbComments = 0;
307         }
308         else
309         {
310             FREE(cellsStrings);
311         }
312     }
313     else
314     {
315         result = (csvResult*)(MALLOC(sizeof(csvResult)));
316         if (result)
317         {
318             result->err = CSV_READ_COLUMNS_ERROR;
319             result->m = 0;
320             result->n = 0;
321             result->pstrValues = NULL;
322             result->pstrComments = NULL;
323             result->nbComments = 0;
324         }
325     }
326     return result;
327 }
328 // =============================================================================
329 void freeCsvResult(csvResult *result)
330 {
331     if (result)
332     {
333         if (result->pstrValues)
334         {
335             freeArrayOfString(result->pstrValues, result->m * result->n);
336             result->pstrValues = NULL;
337         }
338         result->m = 0;
339         result->n = 0;
340
341         if (result->pstrComments)
342         {
343             freeArrayOfString(result->pstrComments, result->nbComments);
344             result->pstrComments = NULL;
345         }
346         result->err = CSV_READ_ERROR;
347         FREE(result);
348         result = NULL;
349     }
350 }
351 // =============================================================================
352 static int getNumbersOfColumnsInLines(const char **lines, int sizelines,
353                                       const char *separator)
354 {
355     int previousNbColumns = 0;
356     int NbColumns = 0;
357     BOOL firstLine = TRUE;
358     if (lines)
359     {
360         int i = 0;
361         for (i = 0; i < sizelines; i++)
362         {
363             NbColumns = getNumbersOfColumnsInLine(lines[i], separator);
364             if (firstLine)
365             {
366                 previousNbColumns = NbColumns;
367                 firstLine = FALSE;
368             }
369             else
370             {
371                 if (previousNbColumns != NbColumns)
372                 {
373                     Sciwarning(_("%s: Inconsistency found in the columns. At line %d, found %d columns while the previous had %d.\n"), _("Warning"), i + 1, NbColumns, previousNbColumns);
374                     return 0;
375                 }
376             }
377         }
378     }
379     return NbColumns;
380 }
381 // =============================================================================
382 static int getNumbersOfColumnsInLine(const char *line, const char *separator)
383 {
384     if (line && separator)
385     {
386         int i = 0;
387         int nbTokens = 0;
388         char **splittedStr = splitLineCSV(line, separator, &nbTokens);
389         if (splittedStr)
390         {
391             freeArrayOfString(splittedStr, nbTokens);
392             return nbTokens;
393         }
394         else
395         {
396             int len = (int)strlen(line);
397             if (len > 0)
398             {
399                 nbTokens = 1;
400                 return nbTokens;
401             }
402         }
403     }
404     return 0;
405 }
406 // =============================================================================
407 static char **getStringsFromLines(const char **lines, int sizelines,
408                                   const char *separator,
409                                   const char *decimal,
410                                   int m, int n)
411 {
412     char **results = NULL;
413
414     if (lines == NULL)
415     {
416         return NULL;
417     }
418     if (separator == NULL)
419     {
420         return NULL;
421     }
422     if (m == 0 || n == 0)
423     {
424         return NULL;
425     }
426
427     results = (char**) MALLOC(sizeof(char*) * (m * n));
428     if (results)
429     {
430         int i = 0;
431         for (i = 0; i < sizelines; i++)
432         {
433             int nbTokens = 0;
434             char **lineStrings = splitLineCSV(lines[i], separator, &nbTokens);
435             int j = 0;
436
437             if (lineStrings == NULL)
438             {
439                 lineStrings = (char**)MALLOC(sizeof(char*) * 1);
440                 lineStrings[0] = os_strdup(lines[i]);
441                 nbTokens = 1;
442             }
443
444             if (m != nbTokens)
445             {
446                 freeArrayOfString(results, nbTokens * n);
447                 FREE(lineStrings);
448                 return NULL;
449             }
450
451             for (j = 0; j < m; j++)
452             {
453
454                 if (!decimal)
455                 {
456                     results[i + n * j] = os_strdup(lineStrings[j]);
457                 }
458                 else
459                 {
460                     /* Proceed to the remplacement of the provided decimal to the default on
461                      * usually, it converts "," => "." */
462                     results[i + n * j] = strsub(lineStrings[j], decimal, getCsvDefaultDecimal());
463                 }
464
465                 if (lineStrings[j])
466                 {
467                     FREE(lineStrings[j]);
468                     lineStrings[j] = NULL;
469                 }
470             }
471             FREE(lineStrings);
472         }
473     }
474     return results;
475 }
476 // =============================================================================
477 static char **removeEmptyLinesAtTheEnd(const char **lines, int *sizelines)
478 {
479     char **returnedLines = NULL;
480     int nbLinesToRemove = 0;
481
482     if (lines)
483     {
484         int i = 0;
485         if (*sizelines >= 1)
486         {
487             for (i = *sizelines - 1; i >= 0; i--)
488             {
489                 char *cleanedLine = stripCharacters(lines[i]);
490                 if (cleanedLine)
491                 {
492                     int len = (int) strlen(cleanedLine);
493                     FREE(cleanedLine);
494                     cleanedLine = NULL;
495                     if (len == 0)
496                     {
497                         nbLinesToRemove++;
498                         FREE((char*)lines[i]);
499                         lines[i] = NULL;
500                     }
501                     else
502                     {
503                         break;
504                     }
505                 }
506             }
507
508             if (nbLinesToRemove > 0)
509             {
510                 *sizelines = *sizelines - nbLinesToRemove;
511             }
512             returnedLines = (char **)MALLOC(sizeof(char *) * (*sizelines));
513             if (returnedLines)
514             {
515                 for (i = 0; i < *sizelines; i++)
516                 {
517                     returnedLines[i] = os_strdup(lines[i]);
518                 }
519             }
520         }
521     }
522
523     return returnedLines;
524 }
525 // =============================================================================
526 static char **removeAllBlankLines(const char **lines, int *sizelines)
527 {
528     char **returnedLines = NULL;
529     int nbLines = 0;
530     if (lines)
531     {
532         int i = 0;
533         for (i = 0; i < *sizelines; i++)
534         {
535             char *cleanedLine = stripCharacters(lines[i]);
536             if (cleanedLine)
537             {
538                 int len = (int) strlen(cleanedLine);
539                 FREE(cleanedLine);
540                 cleanedLine = NULL;
541                 if (len != 0)
542                 {
543                     if (nbLines == 0)
544                     {
545                         nbLines++;
546                         returnedLines = (char**)MALLOC(sizeof(char*) * nbLines);
547                     }
548                     else
549                     {
550                         nbLines++;
551                         returnedLines = (char**)REALLOC(returnedLines, sizeof(char*) * nbLines);
552                     }
553
554                     if (returnedLines)
555                     {
556                         returnedLines[nbLines - 1] = os_strdup(lines[i]);
557                     }
558                     else
559                     {
560                         *sizelines = 0;
561                         return NULL;
562                     }
563                 }
564             }
565         }
566         *sizelines = nbLines;
567     }
568     return returnedLines;
569 }
570 // =============================================================================
571 static char *stripCharacters(const char *line)
572 {
573     char *returnedLine = NULL;
574     if (line)
575     {
576         char *tmpLineWithoutTab = strsub((char*)line, "\t", "");
577         if (tmpLineWithoutTab)
578         {
579             char *tmpLineWithoutLF = strsub(tmpLineWithoutTab, "\r", "");
580             if (tmpLineWithoutLF)
581             {
582                 char *tmpLineWithoutCR = strsub(tmpLineWithoutTab, "\n", "");
583                 if (tmpLineWithoutCR)
584                 {
585                     returnedLine = strsub(tmpLineWithoutCR, " ", "");
586                     FREE(tmpLineWithoutCR);
587                 }
588                 else
589                 {
590                     returnedLine = os_strdup(line);
591                 }
592                 FREE(tmpLineWithoutLF);
593                 tmpLineWithoutLF = NULL;
594             }
595             else
596             {
597                 returnedLine = os_strdup(line);
598             }
599             FREE(tmpLineWithoutTab);
600             tmpLineWithoutTab = NULL;
601         }
602         else
603         {
604             returnedLine = os_strdup(line);
605         }
606     }
607
608     return returnedLine;
609 }
610 // =============================================================================
611 static char **replaceStrings(const char **lines, int nbLines, const char **toreplace, int sizetoreplace)
612 {
613     char **replacedStrings = NULL;
614     int nr = 0;
615
616     nr = sizetoreplace / 2;
617
618     if (lines)
619     {
620         int i = 0;
621
622         replacedStrings = (char**)MALLOC(sizeof(char*) * nbLines);
623         if (replacedStrings)
624         {
625             // Copy the source lines to the target replacedStrings.
626             int j = 0;
627             for (j = 0; j < nbLines; j++)
628             {
629                 replacedStrings[j] = os_strdup(lines[j]);
630             }
631             // Make replacements within the target replacedStrings.
632             for (i = 0; i < nr; i++)
633             {
634                 for (j = 0; j < nbLines; j++)
635                 {
636                     replacedStrings[j] = strsub(replacedStrings[j], toreplace[i], toreplace[nr + i]);
637                 }
638             }
639         }
640     }
641     return replacedStrings;
642 }
643 // =============================================================================
644 static char **extractComments(const char **lines, int nbLines,
645                               const char *regexpcomments, int *nbcomments, int *iErr)
646 {
647     char **pComments = NULL;
648     int i = 0;
649
650     for (i = 0; i < nbLines; i++)
651     {
652         int Output_Start = 0;
653         int Output_End = 0;
654         pcre_error_code answer = pcre_private((char*)lines[i], (char*)regexpcomments, &Output_Start, &Output_End, NULL, NULL);
655
656         if ( (answer == CAN_NOT_COMPILE_PATTERN) || (answer == DELIMITER_NOT_ALPHANUMERIC))
657         {
658             if (pComments)
659             {
660                 freeArrayOfString(pComments, *nbcomments);
661             }
662
663             *nbcomments = 0;
664
665             *iErr = answer;
666             return NULL;
667         }
668
669         if ( answer == PCRE_FINISHED_OK )
670         {
671             (*nbcomments)++;
672             if (pComments == NULL)
673             {
674                 pComments = (char **)MALLOC(sizeof(char*) * (*nbcomments));
675             }
676             else
677             {
678                 pComments = (char **)REALLOC(pComments, sizeof(char*) * (*nbcomments));
679             }
680
681             if (pComments == NULL)
682             {
683                 *nbcomments = 0;
684                 *iErr = 1;
685                 return NULL;
686             }
687             pComments[(*nbcomments) - 1] = os_strdup(lines[i]);
688         }
689     }
690
691     return pComments;
692 }
693 // =============================================================================
694 static char **removeComments(const char **lines, int nbLines,
695                              const char *regexpcomments, int *newNbLines, int *iErr)
696 {
697     char **pLinesCleaned = NULL;
698
699     int i = 0;
700     *newNbLines = 0;
701
702     for (i = 0; i < nbLines; i++)
703     {
704         int Output_Start = 0;
705         int Output_End = 0;
706         pcre_error_code answer = pcre_private((char*)lines[i], (char*)regexpcomments, &Output_Start, &Output_End, NULL, NULL);
707         if ( answer == PCRE_FINISHED_OK )
708         {
709             FREE((char*)lines[i]);
710             lines[i] = NULL;
711         }
712         else
713         {
714             (*newNbLines)++;
715             if (pLinesCleaned == NULL)
716             {
717                 pLinesCleaned = (char **)MALLOC(sizeof(char*) * (*newNbLines));
718             }
719             else
720             {
721                 pLinesCleaned = (char **)REALLOC(pLinesCleaned, sizeof(char*) * (*newNbLines));
722             }
723
724             if (pLinesCleaned == NULL)
725             {
726                 *newNbLines = 0;
727                 *iErr = 1;
728                 return NULL;
729             }
730
731             pLinesCleaned[(*newNbLines) - 1] = (char*)lines[i];
732         }
733     }
734     return pLinesCleaned;
735 }
736 // =============================================================================