1 /************* TabFmt C++ Program Source Code File (.CPP) **************/
2 /* PROGRAM NAME: TABFMT                                                */
3 /* -------------                                                       */
4 /*  Version 3.9.3                                                      */
5 /*                                                                     */
6 /* COPYRIGHT:                                                          */
7 /* ----------                                                          */
8 /*  (C) Copyright to the author Olivier BERTRAND          2001 - 2019  */
9 /*                                                                     */
10 /* WHAT THIS PROGRAM DOES:                                             */
11 /* -----------------------                                             */
12 /*  This program are the TABFMT classes DB execution routines.         */
13 /*  The base class CSV is comma separated files.                       */
14 /*  FMT (Formatted) files are those having a complex internal record   */
15 /*  format described in the Format keyword of their definition.        */
16 /***********************************************************************/
17 
18 /***********************************************************************/
19 /*  Include relevant MariaDB header file.                              */
20 /***********************************************************************/
21 #include "my_global.h"
22 
23 #if defined(_WIN32)
24 #include <io.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <locale.h>
28 #if defined(__BORLANDC__)
29 #define __MFC_COMPAT__                   // To define min/max as macro
30 #endif
31 //#include <windows.h>
32 #include "osutil.h"
33 #else
34 #if defined(UNIX)
35 #include <errno.h>
36 #include <unistd.h>
37 #include "osutil.h"
38 #else
39 #include <io.h>
40 #endif
41 #include <fcntl.h>
42 #endif
43 
44 /***********************************************************************/
45 /*  Include application header files:                                  */
46 /*  global.h    is header containing all global declarations.          */
47 /*  plgdbsem.h  is header containing the DB application declarations.  */
48 /*  tabdos.h    is header containing the TABDOS class declarations.    */
49 /***********************************************************************/
50 #include "global.h"
51 #include "plgdbsem.h"
52 #include "mycat.h"
53 #include "filamap.h"
54 #if defined(GZ_SUPPORT)
55 #include "filamgz.h"
56 #endif   // GZ_SUPPORT
57 #if defined(ZIP_SUPPORT)
58 #include "filamzip.h"
59 #endif   // ZIP_SUPPORT
60 #include "tabfmt.h"
61 #include "tabmul.h"
62 #define  NO_FUNC
63 #include "plgcnx.h"                       // For DB types
64 #include "resource.h"
65 
66 /***********************************************************************/
67 /*  This should be an option.                                          */
68 /***********************************************************************/
69 #define MAXCOL          200        /* Default max column nb in result  */
70 #define TYPE_UNKNOWN     12        /* Must be greater than other types */
71 
72 /***********************************************************************/
73 /*  External function.                                                 */
74 /***********************************************************************/
75 USETEMP UseTemp(void);
76 
77 /***********************************************************************/
78 /* CSVColumns: constructs the result blocks containing the description */
79 /* of all the columns of a CSV file that will be retrieved by #GetData.*/
80 /* Note: the algorithm to set the type is based on the internal values */
81 /* of types (TYPE_STRING < TYPE_DOUBLE < TYPE_INT) (1 < 2 < 7).        */
82 /* If these values are changed, this will have to be revisited.        */
83 /***********************************************************************/
CSVColumns(PGLOBAL g,PCSZ dp,PTOS topt,bool info)84 PQRYRES CSVColumns(PGLOBAL g, PCSZ dp, PTOS topt, bool info)
85   {
86   static int  buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING,
87                           TYPE_INT,   TYPE_INT, TYPE_SHORT};
88   static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE,   FLD_TYPENAME,
89                           FLD_PREC, FLD_LENGTH, FLD_SCALE};
90   static unsigned int length[] = {6, 6, 8, 10, 10, 6};
91 	const char *fn;
92 	char    sep, q;
93 	int     rc, mxr;
94 	bool    hdr;
95   char   *p, *colname[MAXCOL], dechar, buf[8];
96   int     i, imax, hmax, n, nerr, phase, blank, digit, dec, type;
97   int     ncol = sizeof(buftyp) / sizeof(int);
98   int     num_read = 0, num_max = 10000000;     // Statistics
99   int     len[MAXCOL], typ[MAXCOL], prc[MAXCOL];
100 	PCSVDEF tdp;
101 	PTDBCSV tcvp;
102 	PTDBASE tdbp;
103 	PQRYRES qrp;
104   PCOLRES crp;
105 
106   if (info) {
107     imax = hmax = 0;
108     length[0] = 128;
109     goto skipit;
110     } // endif info
111 
112 	//if (GetIntegerTableOption(g, topt, "Multiple", 0)) {
113 	//	strcpy(g->Message, "Cannot find column definition for multiple table");
114 	//	return NULL;
115 	//}	// endif Multiple
116 
117 //      num_max = atoi(p+1);             // Max num of record to test
118   imax = hmax = nerr = 0;
119 
120   for (i = 0; i < MAXCOL; i++) {
121     colname[i] = NULL;
122     len[i] = 0;
123     typ[i] = TYPE_UNKNOWN;
124     prc[i] = 0;
125     } // endfor i
126 
127   /*********************************************************************/
128   /*  Get the CSV table description block.                             */
129   /*********************************************************************/
130 	tdp = new(g) CSVDEF;
131 	tdp->Database = dp;
132 
133 	if ((tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false))) {
134 #if defined(ZIP_SUPPORT)
135 		tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL);
136 		tdp->Mulentries = (tdp->Entry)
137 			              ? strchr(tdp->Entry, '*') || strchr(tdp->Entry, '?')
138 			              : GetBooleanTableOption(g, topt, "Mulentries", false);
139 #else   // !ZIP_SUPPORT
140 		strcpy(g->Message, "ZIP not supported by this version");
141 		return NULL;
142 #endif  // !ZIP_SUPPORT
143 	} // endif // Zipped
144 
145 	fn = tdp->Fn = GetStringTableOption(g, topt, "Filename", NULL);
146 
147 	if (!tdp->Fn) {
148 		strcpy(g->Message, MSG(MISSING_FNAME));
149 		return NULL;
150 	} // endif Fn
151 
152 	if (!(tdp->Lrecl = GetIntegerTableOption(g, topt, "Lrecl", 0)))
153 		tdp->Lrecl = 4096;
154 
155 	tdp->Multiple = GetIntegerTableOption(g, topt, "Multiple", 0);
156 	p = (char*)GetStringTableOption(g, topt, "Separator", ",");
157 	tdp->Sep = (strlen(p) == 2 && p[0] == '\\' && p[1] == 't') ? '\t' : *p;
158 
159 #if defined(_WIN32)
160 	if (tdp->Sep == ',' || strnicmp(setlocale(LC_NUMERIC, NULL), "French", 6))
161 		dechar = '.';
162 	else
163 		dechar = ',';
164 #else   // !_WIN32
165 	dechar = '.';
166 #endif  // !_WIN32
167 
168 	sep = tdp->Sep;
169 	tdp->Quoted = GetIntegerTableOption(g, topt, "Quoted", -1);
170 	p = (char*)GetStringTableOption(g, topt, "Qchar", "");
171 	tdp->Qot = *p;
172 
173 	if (tdp->Qot && tdp->Quoted < 0)
174 		tdp->Quoted = 0;
175 	else if (!tdp->Qot && tdp->Quoted >= 0)
176 		tdp->Qot = '"';
177 
178 	q = tdp->Qot;
179 	hdr = GetBooleanTableOption(g, topt, "Header", false);
180 	tdp->Maxerr = GetIntegerTableOption(g, topt, "Maxerr", 0);
181 	tdp->Accept = GetBooleanTableOption(g, topt, "Accept", false);
182 
183 	if (tdp->Accept && tdp->Maxerr == 0)
184 		tdp->Maxerr = INT_MAX32;       // Accept all bad lines
185 
186 	mxr = MY_MAX(0, tdp->Maxerr);
187 
188 	if (trace(1))
189 		htrc("File %s Sep=%c Qot=%c Header=%d maxerr=%d\n",
190 		SVP(tdp->Fn), tdp->Sep, tdp->Qot, tdp->Header, tdp->Maxerr);
191 
192 #if defined(ZIP_SUPPORT)
193 	if (tdp->Zipped)
194 		tcvp = new(g)TDBCSV(tdp, new(g)UNZFAM(tdp));
195 	else
196 #endif   // ZIP_SUPPORT
197 		tcvp = new(g) TDBCSV(tdp, new(g) DOSFAM(tdp));
198 
199 	tcvp->SetMode(MODE_READ);
200 
201 	if (tdp->Multiple) {
202 		tdbp = new(g)TDBMUL(tcvp);
203 		tdbp->SetMode(MODE_READ);
204 	} else
205 	  tdbp = tcvp;
206 
207 	/*********************************************************************/
208 	/*  Open the CSV file.                                               */
209 	/*********************************************************************/
210 	if (tdbp->OpenDB(g))
211 		return NULL;
212 
213   if (hdr) {
214     /*******************************************************************/
215     /*  Make the column names from the first line.                     */
216     /*******************************************************************/
217     phase = 0;
218 
219     if ((rc = tdbp->ReadDB(g)) == RC_OK) {
220 			p = PlgDBDup(g, tcvp->To_Line);
221 
222       //skip leading blanks
223       for (; *p == ' '; p++) ;
224 
225       if (q && *p == q) {
226         // Header is quoted
227         p++;
228         phase = 1;
229         } // endif q
230 
231       colname[0] = p;
232     } else if (rc == RC_EF) {
233       sprintf(g->Message, MSG(FILE_IS_EMPTY), fn);
234       goto err;
235 		} else
236 			goto err;
237 
238     for (i = 1; *p; p++)
239       if (phase == 1 && *p == q) {
240         *p = '\0';
241         phase = 0;
242       } else if (*p == sep && !phase) {
243         *p = '\0';
244 
245         //skip leading blanks
246         for (; *(p+1) == ' '; p++) ;
247 
248         if (q && *(p+1) == q) {
249           // Header is quoted
250           p++;
251           phase = 1;
252           } // endif q
253 
254         colname[i++] = p + 1;
255         } // endif sep
256 
257     num_read++;
258     imax = hmax = i;
259 
260     for (i = 0; i < hmax; i++)
261       length[0] = MY_MAX(length[0], strlen(colname[i]));
262 
263 		tcvp->Header = true;			// In case of multiple table
264     } // endif hdr
265 
266   for (num_read++; num_read <= num_max; num_read++) {
267     /*******************************************************************/
268     /*  Now start the reading process. Read one line.                  */
269     /*******************************************************************/
270 		if ((rc = tdbp->ReadDB(g)) == RC_OK) {
271     } else if (rc == RC_EF) {
272       sprintf(g->Message, MSG(EOF_AFTER_LINE), num_read -1);
273       break;
274     } else {
275       sprintf(g->Message, MSG(ERR_READING_REC), num_read, fn);
276       goto err;
277     } // endif's
278 
279     /*******************************************************************/
280     /*  Make the test for field lengths.                               */
281     /*******************************************************************/
282     i = n = phase = blank = digit = dec = 0;
283 
284     for (p = tcvp->To_Line; *p; p++)
285       if (*p == sep) {
286         if (phase != 1) {
287           if (i == MAXCOL - 1) {
288             sprintf(g->Message, MSG(TOO_MANY_FIELDS), num_read, fn);
289             goto err;
290             } // endif i
291 
292           if (n) {
293             len[i] = MY_MAX(len[i], n);
294             type = (digit || (dec && n == 1)) ? TYPE_STRING
295                  : (dec) ? TYPE_DOUBLE : TYPE_INT;
296             typ[i] = MY_MIN(type, typ[i]);
297             prc[i] = MY_MAX((typ[i] == TYPE_DOUBLE) ? (dec - 1) : 0, prc[i]);
298             } // endif n
299 
300           i++;
301           n = phase = blank = digit = dec = 0;
302         } else          // phase == 1
303           n++;
304 
305       } else if (*p == ' ') {
306         if (phase < 2)
307           n++;
308 
309         if (blank)
310           digit = 1;
311 
312       } else if (*p == q) {
313         if (phase == 0) {
314           if (blank) {
315             if (++nerr > mxr) {
316               sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read);
317               goto err;
318             } else
319               goto skip;
320           }
321 
322           n = 0;
323           phase = digit = 1;
324         } else if (phase == 1) {
325           if (*(p+1) == q) {
326             // This is currently not implemented for CSV tables
327 //          if (++nerr > mxr) {
328 //            sprintf(g->Message, MSG(QUOTE_IN_QUOTE), num_read);
329 //            goto err;
330 //          } else
331 //            goto skip;
332 
333             p++;
334             n++;
335           } else
336             phase = 2;
337 
338         } else if (++nerr > mxr) {      // phase == 2
339           sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read);
340           goto err;
341         } else
342           goto skip;
343 
344       } else {
345         if (phase == 2) {
346           if (++nerr > mxr) {
347             sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read);
348             goto err;
349           } else
350             goto skip;
351         }
352 
353         // isdigit cannot be used here because of debug assert
354         if (!strchr("0123456789", *p)) {
355           if (!digit && *p == dechar)
356             dec = 1;                    // Decimal point found
357           else if (blank || !(*p == '-' || *p == '+'))
358             digit = 1;
359 
360         } else if (dec)
361           dec++;                        // More decimals
362 
363         n++;
364         blank = 1;
365       } // endif's *p
366 
367     if (phase == 1) {
368       if (++nerr > mxr) {
369         sprintf(g->Message, MSG(UNBALANCE_QUOTE), num_read);
370         goto err;
371       } else
372         goto skip;
373     }
374 
375     if (n) {
376       len[i] = MY_MAX(len[i], n);
377       type = (digit || n == 0 || (dec && n == 1)) ? TYPE_STRING
378            : (dec) ? TYPE_DOUBLE : TYPE_INT;
379       typ[i] = MY_MIN(type, typ[i]);
380       prc[i]  = MY_MAX((typ[i] == TYPE_DOUBLE) ? (dec - 1) : 0, prc[i]);
381       } // endif n
382 
383     imax = MY_MAX(imax, i+1);
384    skip: ;                  // Skip erroneous line
385     } // endfor num_read
386 
387   if (trace(1)) {
388     htrc("imax=%d Lengths:", imax);
389 
390     for (i = 0; i < imax; i++)
391       htrc(" %d", len[i]);
392 
393     htrc("\n");
394   } // endif trace
395 
396 	tdbp->CloseDB(g);
397 
398  skipit:
399   if (trace(1))
400     htrc("CSVColumns: imax=%d hmax=%d len=%d\n",
401                       imax, hmax, length[0]);
402 
403   /*********************************************************************/
404   /*  Allocate the structures used to refer to the result set.         */
405   /*********************************************************************/
406   qrp = PlgAllocResult(g, ncol, imax, IDS_COLUMNS + 3,
407                           buftyp, fldtyp, length, false, false);
408   if (info || !qrp)
409     return qrp;
410 
411   qrp->Nblin = imax;
412 
413   /*********************************************************************/
414   /*  Now get the results into blocks.                                 */
415   /*********************************************************************/
416   for (i = 0; i < imax; i++) {
417     if (i >= hmax) {
418       sprintf(buf, "COL%.3d", i+1);
419       p = buf;
420     } else
421       p = colname[i];
422 
423     if (typ[i] == TYPE_UNKNOWN)            // Void column
424       typ[i] = TYPE_STRING;
425 
426     crp = qrp->Colresp;                    // Column Name
427     crp->Kdata->SetValue(p, i);
428     crp = crp->Next;                       // Data Type
429     crp->Kdata->SetValue(typ[i], i);
430     crp = crp->Next;                       // Type Name
431     crp->Kdata->SetValue(GetTypeName(typ[i]), i);
432     crp = crp->Next;                       // Precision
433     crp->Kdata->SetValue(len[i], i);
434     crp = crp->Next;                       // Length
435     crp->Kdata->SetValue(len[i], i);
436     crp = crp->Next;                       // Scale (precision)
437     crp->Kdata->SetValue(prc[i], i);
438     } // endfor i
439 
440   /*********************************************************************/
441   /*  Return the result pointer for use by GetData routines.           */
442   /*********************************************************************/
443   return qrp;
444 
445  err:
446   tdbp->CloseDB(g);
447   return NULL;
448   } // end of CSVCColumns
449 
450 /* --------------------------- Class CSVDEF -------------------------- */
451 
452 /***********************************************************************/
453 /*  CSVDEF constructor.                                                */
454 /***********************************************************************/
CSVDEF(void)455 CSVDEF::CSVDEF(void)
456   {
457   Fmtd = Header = false;
458 //Maxerr = 0;
459   Quoted = -1;
460   Sep = ',';
461   Qot = '\0';
462   }  // end of CSVDEF constructor
463 
464 /***********************************************************************/
465 /*  DefineAM: define specific AM block values from XDB file.           */
466 /***********************************************************************/
DefineAM(PGLOBAL g,LPCSTR am,int poff)467 bool CSVDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
468   {
469   char   buf[8];
470 
471   // Double check correctness of offset values
472   if (Catfunc == FNC_NO)
473     for (PCOLDEF cdp = To_Cols; cdp; cdp = cdp->GetNext())
474       if (cdp->GetOffset() < 1 && !cdp->IsSpecial()) {
475         strcpy(g->Message, MSG(BAD_OFFSET_VAL));
476         return true;
477         } // endif Offset
478 
479   // Call DOSDEF DefineAM with am=CSV so FMT is not confused with FIX
480   if (DOSDEF::DefineAM(g, "CSV", poff))
481     return true;
482 
483 	Recfm = RECFM_CSV;
484   GetCharCatInfo("Separator", ",", buf, sizeof(buf));
485   Sep = (strlen(buf) == 2 && buf[0] == '\\' && buf[1] == 't') ? '\t' : *buf;
486   Quoted = GetIntCatInfo("Quoted", -1);
487   GetCharCatInfo("Qchar", "", buf, sizeof(buf));
488   Qot = *buf;
489 
490   if (Qot && Quoted < 0)
491     Quoted = 0;
492   else if (!Qot && Quoted >= 0)
493     Qot = '"';
494 
495   Fmtd = (!Sep || (am && (*am == 'F' || *am == 'f')));
496   Header = GetBoolCatInfo("Header", false);
497   Maxerr = GetIntCatInfo("Maxerr", 0);
498   Accept = GetBoolCatInfo("Accept", false);
499 
500   if (Accept && Maxerr == 0)
501     Maxerr = INT_MAX32;       // Accept all bad lines
502 
503   return false;
504   } // end of DefineAM
505 
506 /***********************************************************************/
507 /*  GetTable: makes a new Table Description Block.                     */
508 /***********************************************************************/
GetTable(PGLOBAL g,MODE mode)509 PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode)
510   {
511   PTDBASE tdbp;
512 
513   if (Catfunc != FNC_COL) {
514     USETEMP tmp = UseTemp();
515     bool    map = Mapped && mode != MODE_INSERT &&
516                   !(tmp != TMP_NO && mode == MODE_UPDATE) &&
517                   !(tmp == TMP_FORCE &&
518                   (mode == MODE_UPDATE || mode == MODE_DELETE));
519     PTXF    txfp;
520 
521     /*******************************************************************/
522     /*  Allocate a file processing class of the proper type.           */
523     /*******************************************************************/
524 		if (Zipped) {
525 #if defined(ZIP_SUPPORT)
526 			if (mode == MODE_READ || mode == MODE_ANY || mode == MODE_ALTER) {
527 				txfp = new(g) UNZFAM(this);
528 			} else if (mode == MODE_INSERT) {
529 				txfp = new(g) ZIPFAM(this);
530 			} else {
531 				strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
532 				return NULL;
533 			}	// endif's mode
534 #else   // !ZIP_SUPPORT
535 			strcpy(g->Message, "ZIP not supported");
536 			return NULL;
537 #endif  // !ZIP_SUPPORT
538 		} else if (map) {
539       // Should be now compatible with UNIX
540       txfp = new(g) MAPFAM(this);
541     } else if (Compressed) {
542 #if defined(GZ_SUPPORT)
543       if (Compressed == 1)
544         txfp = new(g) GZFAM(this);
545       else
546         txfp = new(g) ZLBFAM(this);
547 
548 #else   // !GZ_SUPPORT
549         strcpy(g->Message, "Compress not supported");
550         return NULL;
551 #endif  // !GZ_SUPPORT
552     } else
553       txfp = new(g) DOSFAM(this);
554 
555     /*******************************************************************/
556     /*  Allocate a TDB of the proper type.                             */
557     /*  Column blocks will be allocated only when needed.              */
558     /*******************************************************************/
559     if (!Fmtd)
560       tdbp = new(g) TDBCSV(this, txfp);
561     else
562       tdbp = new(g) TDBFMT(this, txfp);
563 
564     if (Multiple)
565       tdbp = new(g) TDBMUL(tdbp);
566     else
567       /*****************************************************************/
568       /*  For block tables, get eventually saved optimization values.  */
569       /*****************************************************************/
570       if (tdbp->GetBlockValues(g)) {
571         PushWarning(g, tdbp);
572 //      return NULL;          // causes a crash when deleting index
573       } else {
574         if (IsOptimized()) {
575           if (map) {
576             txfp = new(g) MBKFAM(this);
577           } else if (Compressed) {
578 #if defined(GZ_SUPPORT)
579             if (Compressed == 1)
580               txfp = new(g) ZBKFAM(this);
581             else {
582               txfp->SetBlkPos(To_Pos);
583               ((PZLBFAM)txfp)->SetOptimized(To_Pos != NULL);
584               } // endelse
585 #else
586             sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "GZ");
587             return NULL;
588 #endif
589           } else
590             txfp = new(g) BLKFAM(this);
591 
592           ((PTDBDOS)tdbp)->SetTxfp(txfp);
593           } // endif Optimized
594 
595       } // endelse
596 
597   } else
598     tdbp = new(g)TDBCCL(this);
599 
600   return tdbp;
601   } // end of GetTable
602 
603 /* -------------------------- Class TDBCSV --------------------------- */
604 
605 /***********************************************************************/
606 /*  Implementation of the TDBCSV class.                                */
607 /***********************************************************************/
TDBCSV(PCSVDEF tdp,PTXF txfp)608 TDBCSV::TDBCSV(PCSVDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
609   {
610 #if defined(_DEBUG)
611   assert (tdp);
612 #endif
613   Field  = NULL;
614   Offset = NULL;
615   Fldlen = NULL;
616   Fields = 0;
617   Nerr = 0;
618   Quoted = tdp->Quoted;
619   Maxerr = tdp->Maxerr;
620   Accept = tdp->Accept;
621   Header = tdp->Header;
622   Sep = tdp->GetSep();
623   Qot = tdp->GetQot();
624   } // end of TDBCSV standard constructor
625 
TDBCSV(PGLOBAL g,PTDBCSV tdbp)626 TDBCSV::TDBCSV(PGLOBAL g, PTDBCSV tdbp) : TDBDOS(g, tdbp)
627   {
628   Fields = tdbp->Fields;
629 
630   if (Fields) {
631     if (tdbp->Offset)
632       Offset = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
633 
634     if (tdbp->Fldlen)
635       Fldlen = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
636 
637     Field = (PSZ *)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields);
638 
639     for (int i = 0; i < Fields; i++) {
640       if (Offset)
641         Offset[i] = tdbp->Offset[i];
642 
643       if (Fldlen)
644         Fldlen[i] = tdbp->Fldlen[i];
645 
646       if (Field) {
647         assert (Fldlen);
648         Field[i] = (PSZ)PlugSubAlloc(g, NULL, Fldlen[i] + 1);
649         Field[i][Fldlen[i]] = '\0';
650         } // endif Field
651 
652       } // endfor i
653 
654   } else {
655     Field  = NULL;
656     Offset = NULL;
657     Fldlen = NULL;
658   } // endif Fields
659 
660   Nerr = tdbp->Nerr;
661   Maxerr = tdbp->Maxerr;
662   Quoted = tdbp->Quoted;
663   Accept = tdbp->Accept;
664   Header = tdbp->Header;
665   Sep = tdbp->Sep;
666   Qot = tdbp->Qot;
667   } // end of TDBCSV copy constructor
668 
669 // Method
Clone(PTABS t)670 PTDB TDBCSV::Clone(PTABS t)
671   {
672   PTDB    tp;
673   PCSVCOL cp1, cp2;
674   PGLOBAL g = t->G;        // Is this really useful ???
675 
676   tp = new(g) TDBCSV(g, this);
677 
678   for (cp1 = (PCSVCOL)Columns; cp1; cp1 = (PCSVCOL)cp1->GetNext()) {
679     cp2 = new(g) CSVCOL(cp1, tp);  // Make a copy
680     NewPointer(t, cp1, cp2);
681     } // endfor cp1
682 
683   return tp;
684   } // end of Clone
685 
686 /***********************************************************************/
687 /*  Allocate CSV column description block.                             */
688 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)689 PCOL TDBCSV::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
690   {
691   return new(g) CSVCOL(g, cdp, this, cprec, n);
692   } // end of MakeCol
693 
694 /***********************************************************************/
695 /*  Check whether the number of errors is greater than the maximum.    */
696 /***********************************************************************/
CheckErr(void)697 bool TDBCSV::CheckErr(void)
698   {
699   return (++Nerr) > Maxerr;
700   } // end of CheckErr
701 
702 /***********************************************************************/
703 /*  CSV EstimatedLength. Returns an estimated minimum line length.     */
704 /***********************************************************************/
EstimatedLength(void)705 int TDBCSV::EstimatedLength(void)
706   {
707   int     n = 0;
708   PCOLDEF cdp;
709 
710   if (trace(1))
711     htrc("EstimatedLength: Fields=%d Columns=%p\n", Fields, Columns);
712 
713   for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext())
714     if (!cdp->IsSpecial() && !cdp->IsVirtual())  // A true column
715       n++;
716 
717   return --n;   // Number of separators if all fields are null
718   } // end of Estimated Length
719 
720 #if 0
721 /***********************************************************************/
722 /*  CSV tables needs the use temporary files for Update.               */
723 /***********************************************************************/
724 bool TDBCSV::IsUsingTemp(PGLOBAL g)
725   {
726   return (Use_Temp == TMP_YES || Use_Temp == TMP_FORCE ||
727          (Use_Temp == TMP_AUTO && Mode == MODE_UPDATE));
728   } // end of IsUsingTemp
729 #endif // 0  (Same as TDBDOS one)
730 
731 /***********************************************************************/
732 /*  CSV Access Method opening routine.                                 */
733 /*  First allocate the Offset and Fldlen arrays according to the       */
734 /*  greatest field used in that query. Then call the DOS opening fnc.  */
735 /***********************************************************************/
OpenDB(PGLOBAL g)736 bool TDBCSV::OpenDB(PGLOBAL g)
737   {
738   bool    rc = false;
739   PCOLDEF cdp;
740   PDOSDEF tdp = (PDOSDEF)To_Def;
741 
742   if (Use != USE_OPEN && (Columns || Mode == MODE_UPDATE)) {
743     // Allocate the storage used to read (or write) records
744     int     i, len;
745     PCSVCOL colp;
746 
747     if (!Fields) {            // May have been set in TABFMT::OpenDB
748       if (Mode != MODE_UPDATE && Mode != MODE_INSERT) {
749         for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next)
750           if (!colp->IsSpecial() && !colp->IsVirtual())
751             Fields = MY_MAX(Fields, (int)colp->Fldnum);
752 
753         if (Columns)
754           Fields++;           // Fldnum was 0 based
755 
756       } else
757         for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
758           if (!cdp->IsSpecial() && !cdp->IsVirtual())
759             Fields++;
760     }
761 
762     Offset = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
763     Fldlen = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
764 
765     if (Mode == MODE_INSERT || Mode == MODE_UPDATE) {
766       Field = (PSZ*)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields);
767       Fldtyp = (bool*)PlugSubAlloc(g, NULL, sizeof(bool) * Fields);
768       } // endif Mode
769 
770     for (i = 0; i < Fields; i++) {
771       Offset[i] = 0;
772       Fldlen[i] = 0;
773 
774       if (Field) {
775         Field[i] = NULL;
776         Fldtyp[i] = false;
777         } // endif Field
778 
779       } // endfor i
780 
781     if (Field) {
782       // Prepare writing fields
783       if (Mode != MODE_UPDATE) {
784         for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next)
785           if (!colp->IsSpecial() && !colp->IsVirtual()) {
786             i = colp->Fldnum;
787             len = colp->GetLength();
788             Field[i] = (PSZ)PlugSubAlloc(g, NULL, len + 1);
789             Field[i][len] = '\0';
790             Fldlen[i] = len;
791             Fldtyp[i] = IsTypeNum(colp->GetResultType());
792             } // endif colp
793 
794       } else     // MODE_UPDATE
795         for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
796           if (!cdp->IsSpecial() && !cdp->IsVirtual()) {
797             i = cdp->GetOffset() - 1;
798             len = cdp->GetLength();
799             Field[i] = (PSZ)PlugSubAlloc(g, NULL, len + 1);
800             Field[i][len] = '\0';
801             Fldlen[i] = len;
802             Fldtyp[i] = IsTypeNum(cdp->GetType());
803             } // endif cdp
804     }
805 
806     } // endif Use
807 
808   if (Header) {
809     // Check that the Lrecl is at least equal to the header line length
810     int     headlen = 0;
811     PCOLDEF cdp;
812     PDOSDEF tdp = (PDOSDEF)To_Def;
813 
814     for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
815       headlen += strlen(cdp->GetName()) + 3;  // 3 if names are quoted
816 
817     if (headlen > Lrecl) {
818       Lrecl = headlen;
819       Txfp->Lrecl = headlen;
820       } // endif headlen
821 
822     } // endif Header
823 
824   Nerr = 0;
825   rc = TDBDOS::OpenDB(g);
826 
827   if (!rc && Mode == MODE_UPDATE && To_Kindex)
828     // Because KINDEX::Init is executed in mode READ, we must restore
829     // the Fldlen array that was modified when reading the table file.
830     for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
831       Fldlen[cdp->GetOffset() - 1] = cdp->GetLength();
832 
833   return rc;
834   } // end of OpenDB
835 
836 /***********************************************************************/
837 /*  SkipHeader: Physically skip first header line if applicable.       */
838 /*  This is called from TDBDOS::OpenDB and must be executed before     */
839 /*  Kindex construction if the file is accessed using an index.        */
840 /***********************************************************************/
SkipHeader(PGLOBAL g)841 bool TDBCSV::SkipHeader(PGLOBAL g)
842   {
843   int len = GetFileLength(g);
844   bool rc = false;
845 
846 #if defined(_DEBUG)
847   if (len < 0)
848     return true;
849 #endif   // _DEBUG
850 
851   if (Header) {
852     if (Mode == MODE_INSERT) {
853       if (!len) {
854         // New file, the header line must be constructed and written
855         int     i, n = 0;
856         int    hlen = 0;
857         bool    q = Qot && Quoted > 0;
858         PCOLDEF cdp;
859 
860         // Estimate the length of the header list
861         for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext()) {
862           hlen += (1 + strlen(cdp->GetName()));
863           hlen += ((q) ? 2 : 0);
864           n++;            // Calculate the number of columns
865           } // endfor cdp
866 
867         if (hlen > Lrecl) {
868           sprintf(g->Message, MSG(LRECL_TOO_SMALL), hlen);
869           return true;
870           } // endif hlen
871 
872         // File is empty, write a header record
873         memset(To_Line, 0, Lrecl);
874 
875         // The column order in the file is given by the offset value
876         for (i = 1; i <= n; i++)
877           for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext())
878             if (cdp->GetOffset() == i) {
879               if (q)
880                 To_Line[strlen(To_Line)] = Qot;
881 
882               strcat(To_Line, cdp->GetName());
883 
884               if (q)
885                 To_Line[strlen(To_Line)] = Qot;
886 
887               if (i < n)
888                 To_Line[strlen(To_Line)] = Sep;
889 
890               } // endif Offset
891 
892         rc = (Txfp->WriteBuffer(g) == RC_FX);
893         } // endif !FileLength
894 
895     } else if (Mode == MODE_DELETE) {
896       if (len)
897         rc = (Txfp->SkipRecord(g, true) == RC_FX);
898 
899     } else if (len) // !Insert && !Delete
900       rc = (Txfp->SkipRecord(g, false) == RC_FX || Txfp->RecordPos(g));
901 
902     } // endif Header
903 
904   return rc;
905   } // end of SkipHeader
906 
907 /***********************************************************************/
908 /*  ReadBuffer: Physical read routine for the CSV access method.       */
909 /***********************************************************************/
ReadBuffer(PGLOBAL g)910 int TDBCSV::ReadBuffer(PGLOBAL g)
911   {
912   //char *p1, *p2, *p = NULL;
913 	char *p2, *p = NULL;
914 	int   i, n, len, rc = Txfp->ReadBuffer(g);
915   bool  bad = false;
916 
917   if (trace(2))
918     htrc("CSV: Row is '%s' rc=%d\n", To_Line, rc);
919 
920   if (rc != RC_OK || !Fields)
921     return rc;
922   else
923     p2 = To_Line;
924 
925   // Find the offsets and lengths of the columns for this row
926   for (i = 0; i < Fields; i++) {
927     if (!bad) {
928       if (Qot && *p2 == Qot) {                // Quoted field
929         //for (n = 0, p1 = ++p2; (p = strchr(p1, Qot)); p1 = p + 2)
930         //  if (*(p + 1) == Qot)
931         //    n++;                              // Doubled internal quotes
932         //  else
933         //    break;                            // Final quote
934 
935 				for (n = 0, p = ++p2; p; p++)
936 					if (*p == Qot || *p == '\\') {
937 						if (*(++p) == Qot)
938 							n++;														// Escaped internal quotes
939 						else if (*(p - 1) == Qot)
940 							break;													// Final quote
941 					}	// endif *p
942 
943         if (p) {
944           //len = p++ - p2;
945 					len = (int)(p - p2 - 1);
946 
947 //        if (Sep != ' ')
948 //          for (; *p == ' '; p++) ;          // Skip blanks
949 
950           if (*p != Sep && i != Fields - 1) { // Should be the separator
951             if (CheckErr()) {
952               sprintf(g->Message, MSG(MISSING_FIELD),
953                                   i+1, Name, RowNumber(g));
954               return RC_FX;
955             } else if (Accept)
956               bad = true;
957             else
958               return RC_NF;
959 
960             } // endif p
961 
962           if (n) {
963             int j, k;
964 
965             // Suppress the escape of internal quotes
966             for (j = k = 0; j < len; j++, k++) {
967               if (p2[j] == Qot || (p2[j] == '\\' && p2[j + 1] == Qot))
968                 j++;                          // skip escape char
969 							else if (p2[j] == '\\')
970 								p2[k++] = p2[j++];						// avoid \\Qot
971 
972               p2[k] = p2[j];
973               } // endfor i, j
974 
975             len -= n;
976             } // endif n
977 
978         } else if (CheckErr()) {
979           sprintf(g->Message, MSG(BAD_QUOTE_FIELD),
980                               Name, i+1, RowNumber(g));
981           return RC_FX;
982         } else if (Accept) {
983           len = strlen(p2);
984           bad = true;
985         } else
986           return RC_NF;
987 
988       } else if ((p = strchr(p2, Sep)))
989         len = (int)(p - p2);
990       else if (i == Fields - 1)
991         len = strlen(p2);
992       else if (Accept && Maxerr == 0) {
993         len = strlen(p2);
994         bad = true;
995       } else if (CheckErr()) {
996         sprintf(g->Message, MSG(MISSING_FIELD), i+1, Name, RowNumber(g));
997         return RC_FX;
998       } else if (Accept) {
999         len = strlen(p2);
1000         bad = true;
1001       } else
1002         return RC_NF;
1003 
1004     } else
1005       len = 0;
1006 
1007     Offset[i] = (int)(p2 - To_Line);
1008 
1009     if (Mode != MODE_UPDATE)
1010       Fldlen[i] = len;
1011     else if (len > Fldlen[i]) {
1012       sprintf(g->Message, MSG(FIELD_TOO_LONG), i+1, RowNumber(g));
1013       return RC_FX;
1014     } else {
1015       strncpy(Field[i], p2, len);
1016       Field[i][len] = '\0';
1017     } // endif Mode
1018 
1019     if (p)
1020       p2 = p + 1;
1021 
1022     } // endfor i
1023 
1024   return rc;
1025   } // end of ReadBuffer
1026 
1027 /***********************************************************************/
1028 /*  Prepare the line to write.                                         */
1029 /***********************************************************************/
PrepareWriting(PGLOBAL g)1030 bool TDBCSV::PrepareWriting(PGLOBAL g)
1031   {
1032   char sep[2], qot[2];
1033   int  i, nlen, oldlen = strlen(To_Line);
1034 
1035   if (trace(2))
1036     htrc("CSV WriteDB: R%d Mode=%d key=%p link=%p\n",
1037           Tdb_No, Mode, To_Key_Col, To_Link);
1038 
1039   // Before writing the line we must check its length
1040   if ((nlen = CheckWrite(g)) < 0)
1041     return true;
1042 
1043   // Before writing the line we must make it
1044   sep[0] = Sep;
1045   sep[1] = '\0';
1046   qot[0] = Qot;
1047   qot[1] = '\0';
1048   *To_Line = '\0';
1049 
1050   for (i = 0; i < Fields; i++) {
1051     if (i)
1052       strcat(To_Line, sep);
1053 
1054     if (Field[i]) {
1055       if (!strlen(Field[i])) {
1056         // Generally null fields are not quoted
1057         if (Quoted > 2)
1058           // Except if explicitely required
1059           strcat(strcat(To_Line, qot), qot);
1060 
1061       } else if (Qot && (strchr(Field[i], Sep) || *Field[i] == Qot
1062               || Quoted > 1 || (Quoted == 1 && !Fldtyp[i]))) {
1063         if (strchr(Field[i], Qot)) {
1064           // Field contains quotes that must be doubled
1065           int j, k = strlen(To_Line), n = strlen(Field[i]);
1066 
1067           To_Line[k++] = Qot;
1068 
1069           for (j = 0; j < n; j++) {
1070             if (Field[i][j] == Qot)
1071               To_Line[k++] = Qot;
1072 
1073             To_Line[k++] = Field[i][j];
1074             } // endfor j
1075 
1076           To_Line[k++] = Qot;
1077           To_Line[k] = '\0';
1078         } else
1079           strcat(strcat(strcat(To_Line, qot), Field[i]), qot);
1080       }
1081 
1082       else
1083         strcat(To_Line, Field[i]);
1084     }
1085 
1086     } // endfor i
1087 
1088 #if defined(_DEBUG)
1089   assert ((unsigned)nlen == strlen(To_Line));
1090 #endif
1091 
1092   if (Mode == MODE_UPDATE && nlen < oldlen
1093                           && !((PDOSFAM)Txfp)->GetUseTemp()) {
1094     // In Update mode with no temp file, line length must not change
1095     To_Line[nlen] = Sep;
1096 
1097     for (nlen++; nlen < oldlen; nlen++)
1098       To_Line[nlen] = ' ';
1099 
1100     To_Line[nlen] = '\0';
1101     } // endif
1102 
1103   if (trace(2))
1104     htrc("Write: line is=%s", To_Line);
1105 
1106   return false;
1107   } // end of PrepareWriting
1108 
1109 /***********************************************************************/
1110 /*  Data Base write routine CSV file access method.                    */
1111 /***********************************************************************/
WriteDB(PGLOBAL g)1112 int TDBCSV::WriteDB(PGLOBAL g)
1113   {
1114   // Before writing the line we must check and prepare it
1115   if (PrepareWriting(g))
1116     return RC_FX;
1117 
1118   /*********************************************************************/
1119   /*  Now start the writing process.                                   */
1120   /*********************************************************************/
1121   return Txfp->WriteBuffer(g);
1122   } // end of WriteDB
1123 
1124 /***********************************************************************/
1125 /*  Check whether a new line fit in the file lrecl size.               */
1126 /***********************************************************************/
CheckWrite(PGLOBAL g)1127 int TDBCSV::CheckWrite(PGLOBAL g)
1128   {
1129   int maxlen, n, nlen = (Fields - 1);
1130 
1131   if (trace(2))
1132     htrc("CheckWrite: R%d Mode=%d\n", Tdb_No, Mode);
1133 
1134   // Before writing the line we must check its length
1135   maxlen = (Mode == MODE_UPDATE && !Txfp->GetUseTemp())
1136          ? strlen(To_Line) : Lrecl;
1137 
1138   // Check whether record is too int
1139   for (int i = 0; i < Fields; i++)
1140     if (Field[i]) {
1141       if (!(n = strlen(Field[i])))
1142         n += (Quoted > 2 ? 2 : 0);
1143       else if (strchr(Field[i], Sep) || (Qot && *Field[i] == Qot)
1144           || Quoted > 1 || (Quoted == 1 && !Fldtyp[i]))
1145         if (!Qot) {
1146           sprintf(g->Message, MSG(SEP_IN_FIELD), i + 1);
1147           return -1;
1148         } else {
1149           // Quotes inside a quoted field must be doubled
1150           char *p1, *p2;
1151 
1152           for (p1 = Field[i]; (p2 = strchr(p1, Qot)); p1 = p2 + 1)
1153             n++;
1154 
1155           n += 2;        // Outside quotes
1156         } // endif
1157 
1158       if ((nlen += n) > maxlen) {
1159         strcpy(g->Message, MSG(LINE_TOO_LONG));
1160         return -1;
1161         } // endif nlen
1162 
1163       } // endif Field
1164 
1165   return nlen;
1166   } // end of CheckWrite
1167 
1168 /* ------------------------------------------------------------------- */
1169 
1170 /***********************************************************************/
1171 /*  Implementation of the TDBFMT class.                                */
1172 /***********************************************************************/
TDBFMT(PGLOBAL g,PTDBFMT tdbp)1173 TDBFMT::TDBFMT(PGLOBAL g, PTDBFMT tdbp) : TDBCSV(g, tdbp)
1174   {
1175   FldFormat = tdbp->FldFormat;
1176   To_Fld = tdbp->To_Fld;
1177   FmtTest = tdbp->FmtTest;
1178   Linenum = tdbp->Linenum;
1179   } // end of TDBFMT copy constructor
1180 
1181 // Method
Clone(PTABS t)1182 PTDB TDBFMT::Clone(PTABS t)
1183   {
1184   PTDB    tp;
1185   PCSVCOL cp1, cp2;
1186 //PFMTCOL cp1, cp2;
1187   PGLOBAL g = t->G;        // Is this really useful ???
1188 
1189   tp = new(g) TDBFMT(g, this);
1190 
1191   for (cp1 = (PCSVCOL)Columns; cp1; cp1 = (PCSVCOL)cp1->GetNext()) {
1192 //for (cp1 = (PFMTCOL)Columns; cp1; cp1 = (PFMTCOL)cp1->GetNext()) {
1193     cp2 = new(g) CSVCOL(cp1, tp);  // Make a copy
1194 //  cp2 = new(g) FMTCOL(cp1, tp);  // Make a copy
1195     NewPointer(t, cp1, cp2);
1196     } // endfor cp1
1197 
1198   return tp;
1199   } // end of Clone
1200 
1201 /***********************************************************************/
1202 /*  Allocate FMT column description block.                             */
1203 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)1204 PCOL TDBFMT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
1205   {
1206   return new(g) CSVCOL(g, cdp, this, cprec, n);
1207 //return new(g) FMTCOL(cdp, this, cprec, n);
1208   } // end of MakeCol
1209 
1210 /***********************************************************************/
1211 /*  FMT EstimatedLength. Returns an estimated minimum line length.     */
1212 /*  The big problem here is how can we astimated that minimum ?        */
1213 /***********************************************************************/
EstimatedLength(void)1214 int TDBFMT::EstimatedLength(void)
1215   {
1216   // This is rather stupid !!!
1217   return ((PDOSDEF)To_Def)->GetEnding() + (int)((Lrecl / 10) + 1);
1218   } // end of EstimatedLength
1219 
1220 /***********************************************************************/
1221 /*  FMT Access Method opening routine.                                 */
1222 /***********************************************************************/
OpenDB(PGLOBAL g)1223 bool TDBFMT::OpenDB(PGLOBAL g)
1224   {
1225   Linenum = 0;
1226 
1227   if (Mode == MODE_INSERT || Mode == MODE_UPDATE) {
1228     sprintf(g->Message, MSG(FMT_WRITE_NIY), "FMT");
1229     return true;                    // NIY
1230     } // endif Mode
1231 
1232   if (Use != USE_OPEN && Columns) {
1233     // Make the formats used to read records
1234     PSZ     pfm;
1235     int     i, n;
1236     PCSVCOL colp;
1237     PCOLDEF cdp;
1238     PDOSDEF tdp = (PDOSDEF)To_Def;
1239 
1240     for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next)
1241       if (!colp->IsSpecial() && !colp->IsVirtual())  // a true column
1242         Fields = MY_MAX(Fields, (int)colp->Fldnum);
1243 
1244     if (Columns)
1245       Fields++;                // Fldnum was 0 based
1246 
1247     To_Fld = PlugSubAlloc(g, NULL, Lrecl + 1);
1248     FldFormat = (PSZ*)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields);
1249     memset(FldFormat, 0, sizeof(PSZ) * Fields);
1250     FmtTest = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
1251     memset(FmtTest, 0, sizeof(int) * Fields);
1252 
1253     // Get the column formats
1254     for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
1255       if (!cdp->IsSpecial() && !cdp->IsVirtual()
1256                             && (i = cdp->GetOffset() - 1) < Fields) {
1257         if (!(pfm = cdp->GetFmt())) {
1258           sprintf(g->Message, MSG(NO_FLD_FORMAT), i + 1, Name);
1259           return true;
1260           } // endif pfm
1261 
1262         // Roughly check the Fmt format
1263         if ((n = strlen(pfm) - 2) < 4) {
1264           sprintf(g->Message, MSG(BAD_FLD_FORMAT), i + 1, Name);
1265           return true;
1266           } // endif n
1267 
1268         FldFormat[i] = (PSZ)PlugSubAlloc(g, NULL, n + 5);
1269         strcpy(FldFormat[i], pfm);
1270 
1271         if (!strcmp(pfm + n, "%m")) {
1272           // This is a field that can be missing. Flag it so it can
1273           // be handled with special processing.
1274           FldFormat[i][n+1] = 'n';  // To have sscanf normal processing
1275           FmtTest[i] = 2;
1276         } else if (i+1 < Fields && strcmp(pfm + n, "%n")) {
1277           // There are trailing characters after the field contents
1278           // add a marker for the next field start position.
1279           strcat(FldFormat[i], "%n");
1280           FmtTest[i] = 1;
1281         } // endif's
1282 
1283         } // endif i
1284 
1285     } // endif Use
1286 
1287   return TDBCSV::OpenDB(g);
1288   } // end of OpenDB
1289 
1290 /***********************************************************************/
1291 /*  ReadBuffer: Physical read routine for the FMT access method.       */
1292 /***********************************************************************/
ReadBuffer(PGLOBAL g)1293 int TDBFMT::ReadBuffer(PGLOBAL g)
1294   {
1295   int  i, len, n, deb, fin, nwp, pos = 0, rc;
1296   bool bad = false;
1297 
1298   if ((rc = Txfp->ReadBuffer(g)) != RC_OK || !Fields)
1299     return rc;
1300   else
1301     ++Linenum;
1302 
1303   if (trace(2))
1304     htrc("FMT: Row %d is '%s' rc=%d\n", Linenum, To_Line, rc);
1305 
1306   // Find the offsets and lengths of the columns for this row
1307   for (i = 0; i < Fields; i++) {
1308     if (!bad) {
1309       deb = fin = -1;
1310 
1311       if (!FldFormat[i]) {
1312         n = 0;
1313       } else if (FmtTest[i] == 1) {
1314         nwp = -1;
1315         n = sscanf(To_Line + pos, FldFormat[i], &deb, To_Fld, &fin, &nwp);
1316       } else {
1317         n = sscanf(To_Line + pos, FldFormat[i], &deb, To_Fld, &fin);
1318 
1319         if (n != 1 && (deb >= 0 || i == Fields - 1) && FmtTest[i] == 2) {
1320           // Missing optional field, not an error
1321           n = 1;
1322 
1323           if (i == Fields - 1)
1324             fin = deb = 0;
1325           else
1326             fin = deb;
1327 
1328           } // endif n
1329 
1330         nwp = fin;
1331       } // endif i
1332 
1333       if (n != 1 || deb < 0 || fin < 0 || nwp < 0) {
1334         // This is to avoid a very strange sscanf bug occuring
1335         // with fields that ends with a null character.
1336         // This bug causes subsequent sscanf to return in error,
1337         // so next lines are not parsed correctly.
1338         sscanf("a", "%*c");       // Seems to reset things Ok
1339 
1340         if (CheckErr()) {
1341           sprintf(g->Message, MSG(BAD_LINEFLD_FMT), Linenum, i + 1, Name);
1342           return RC_FX;
1343         } else if (Accept)
1344           bad = true;
1345         else
1346           return RC_NF;
1347 
1348         } // endif n...
1349 
1350       } // endif !bad
1351 
1352     if (!bad) {
1353       Offset[i] = pos + deb;
1354       len = fin - deb;
1355     } else {
1356       nwp = 0;
1357       Offset[i] = pos;
1358       len = 0;
1359     } // endif bad
1360 
1361 //  if (Mode != MODE_UPDATE)
1362       Fldlen[i] = len;
1363 //  else if (len > Fldlen[i]) {
1364 //    sprintf(g->Message, MSG(FIELD_TOO_LONG), i+1, To_Tdb->RowNumber(g));
1365 //    return RC_FX;
1366 //  } else {
1367 //    strncpy(Field[i], To_Line + pos, len);
1368 //    Field[i][len] = '\0';
1369 //  } // endif Mode
1370 
1371     pos += nwp;
1372     } // endfor i
1373 
1374   if (bad)
1375     Nerr++;
1376   else
1377     sscanf("a", "%*c");             // Seems to reset things Ok
1378 
1379   return rc;
1380   } // end of ReadBuffer
1381 
1382 /***********************************************************************/
1383 /*  Data Base write routine FMT file access method.                    */
1384 /***********************************************************************/
WriteDB(PGLOBAL g)1385 int TDBFMT::WriteDB(PGLOBAL g)
1386   {
1387   sprintf(g->Message, MSG(FMT_WRITE_NIY), "FMT");
1388   return RC_FX;                    // NIY
1389   } // end of WriteDB
1390 
1391 // ------------------------ CSVCOL functions ----------------------------
1392 
1393 /***********************************************************************/
1394 /*  CSVCOL public constructor                                          */
1395 /***********************************************************************/
CSVCOL(PGLOBAL g,PCOLDEF cdp,PTDB tdbp,PCOL cprec,int i)1396 CSVCOL::CSVCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
1397   : DOSCOL(g, cdp, tdbp, cprec, i, "CSV")
1398   {
1399   Fldnum = Deplac - 1;
1400   Deplac = 0;
1401   } // end of CSVCOL constructor
1402 
1403 /***********************************************************************/
1404 /*  CSVCOL constructor used for copying columns.                       */
1405 /*  tdbp is the pointer to the new table descriptor.                   */
1406 /***********************************************************************/
CSVCOL(CSVCOL * col1,PTDB tdbp)1407 CSVCOL::CSVCOL(CSVCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp)
1408   {
1409   Fldnum = col1->Fldnum;
1410   } // end of CSVCOL copy constructor
1411 
1412 /***********************************************************************/
1413 /*  VarSize: This function tells UpdateDB whether or not the block     */
1414 /*  optimization file must be redone if this column is updated, even   */
1415 /*  it is not sorted or clustered. This applies to a blocked table,    */
1416 /*  because if it is updated using a temporary file, the block size    */
1417 /*  may be modified.                                                   */
1418 /***********************************************************************/
VarSize(void)1419 bool CSVCOL::VarSize(void)
1420   {
1421   PTXF txfp = ((PTDBCSV)To_Tdb)->Txfp;
1422 
1423   if (txfp->IsBlocked() && txfp->GetUseTemp())
1424     // Blocked table using a temporary file
1425     return true;
1426   else
1427     return false;
1428 
1429   } // end VarSize
1430 
1431 /***********************************************************************/
1432 /*  ReadColumn: call DOSCOL::ReadColumn after having set the offet     */
1433 /*  and length of the field to read as calculated by TDBCSV::ReadDB.   */
1434 /***********************************************************************/
ReadColumn(PGLOBAL g)1435 void CSVCOL::ReadColumn(PGLOBAL g)
1436   {
1437   int     rc;
1438   PTDBCSV tdbp = (PTDBCSV)To_Tdb;
1439 
1440   /*********************************************************************/
1441   /*  If physical reading of the line was deferred, do it now.         */
1442   /*********************************************************************/
1443   if (!tdbp->IsRead())
1444     if ((rc = tdbp->ReadBuffer(g)) != RC_OK) {
1445       if (rc == RC_EF)
1446         sprintf(g->Message, MSG(INV_DEF_READ), rc);
1447 
1448 			throw 34;
1449 		} // endif
1450 
1451   if (tdbp->Mode != MODE_UPDATE) {
1452     int colen = Long;                    // Column length
1453 
1454     // Set the field offset and length for this row
1455     Deplac = tdbp->Offset[Fldnum];       // Field offset
1456     Long   = tdbp->Fldlen[Fldnum];       // Field length
1457 
1458     if (trace(2))
1459       htrc("CSV ReadColumn %s Fldnum=%d offset=%d fldlen=%d\n",
1460             Name, Fldnum, Deplac, Long);
1461 
1462     if (Long > colen && tdbp->CheckErr()) {
1463       Long = colen;                       // Restore column length
1464       sprintf(g->Message, MSG(FLD_TOO_LNG_FOR),
1465               Fldnum + 1, Name, To_Tdb->RowNumber(g), tdbp->GetFile(g));
1466 			throw 34;
1467 		} // endif Long
1468 
1469     // Now do the reading
1470     DOSCOL::ReadColumn(g);
1471 
1472     // Restore column length
1473     Long = colen;
1474   } else {         // Mode Update
1475     // Field have been copied in TDB Field array
1476     PSZ fp = tdbp->Field[Fldnum];
1477 
1478     if (Dsp)
1479       for (int i = 0; fp[i]; i++)
1480         if (fp[i] == Dsp)
1481           fp[i] = '.';
1482 
1483     Value->SetValue_psz(fp);
1484 
1485     // Set null when applicable
1486     if (Nullable)
1487       Value->SetNull(Value->IsZero());
1488 
1489   } // endif Mode
1490 
1491   } // end of ReadColumn
1492 
1493 /***********************************************************************/
1494 /*  WriteColumn: The column is written in TDBCSV matching Field.       */
1495 /***********************************************************************/
WriteColumn(PGLOBAL g)1496 void CSVCOL::WriteColumn(PGLOBAL g)
1497   {
1498   char   *p;
1499   int     n, flen;
1500   PTDBCSV tdbp = (PTDBCSV)To_Tdb;
1501 
1502   if (trace(2))
1503     htrc("CSV WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
1504           Name, tdbp->GetTdb_No(), ColUse, Status);
1505 
1506   flen = GetLength();
1507 
1508   if (trace(2))
1509     htrc("Lrecl=%d Long=%d field=%d coltype=%d colval=%p\n",
1510           tdbp->Lrecl, Long, flen, Buf_Type, Value);
1511 
1512   /*********************************************************************/
1513   /*  Check whether the new value has to be converted to Buf_Type.     */
1514   /*********************************************************************/
1515   if (Value != To_Val)
1516     Value->SetValue_pval(To_Val, false);    // Convert the updated value
1517 
1518   /*********************************************************************/
1519   /*  Get the string representation of the column value.               */
1520   /*********************************************************************/
1521   p = Value->GetCharString(Buf);
1522 	n = strlen(p);
1523 
1524   if (trace(2))
1525     htrc("new length(%p)=%d\n", p, n);
1526 
1527   if (n > flen) {
1528     sprintf(g->Message, MSG(BAD_FLD_LENGTH), Name, p, n,
1529                         tdbp->RowNumber(g), tdbp->GetFile(g));
1530 		throw 34;
1531 	} else if (Dsp)
1532     for (int i = 0; p[i]; i++)
1533       if (p[i] == '.')
1534         p[i] = Dsp;
1535 
1536   if (trace(2))
1537     htrc("buffer=%s\n", p);
1538 
1539   /*********************************************************************/
1540   /*  Updating must be done also during the first pass so writing the  */
1541   /*  updated record can be checked for acceptable record length.      */
1542   /*********************************************************************/
1543   if (Fldnum < 0) {
1544     // This can happen for wrong offset value in XDB files
1545     sprintf(g->Message, MSG(BAD_FIELD_RANK), Fldnum + 1, Name);
1546 		throw 34;
1547 	} else
1548     strncpy(tdbp->Field[Fldnum], p, flen);
1549 
1550   if (trace(2))
1551     htrc(" col written: '%s'\n", p);
1552 
1553   } // end of WriteColumn
1554 
1555 /* ---------------------------TDBCCL class --------------------------- */
1556 
1557 /***********************************************************************/
1558 /*  TDBCCL class constructor.                                          */
1559 /***********************************************************************/
TDBCCL(PCSVDEF tdp)1560 TDBCCL::TDBCCL(PCSVDEF tdp) : TDBCAT(tdp)
1561 {
1562 	Topt = tdp->GetTopt();
1563 } // end of TDBCCL constructor
1564 
1565 /***********************************************************************/
1566 /*  GetResult: Get the list the CSV file columns.                      */
1567 /***********************************************************************/
GetResult(PGLOBAL g)1568 PQRYRES TDBCCL::GetResult(PGLOBAL g)
1569   {
1570   return CSVColumns(g, ((PTABDEF)To_Def)->GetPath(), Topt, false);
1571   } // end of GetResult
1572 
1573 /* ------------------------ End of TabFmt ---------------------------- */
1574