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