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