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