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