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