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