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