You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1411 lines
44 KiB

  1. /************* TabFmt C++ Program Source Code File (.CPP) **************/
  2. /* PROGRAM NAME: TABFMT */
  3. /* ------------- */
  4. /* Version 3.8 */
  5. /* */
  6. /* COPYRIGHT: */
  7. /* ---------- */
  8. /* (C) Copyright to the author Olivier BERTRAND 2001 - 2013 */
  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. /* Include relevant MariaDB header file. */
  19. /***********************************************************************/
  20. #include "my_global.h"
  21. #if defined(WIN32)
  22. #include <io.h>
  23. #include <fcntl.h>
  24. #include <errno.h>
  25. #include <locale.h>
  26. #if defined(__BORLANDC__)
  27. #define __MFC_COMPAT__ // To define min/max as macro
  28. #endif
  29. //#include <windows.h>
  30. #include "osutil.h"
  31. #else
  32. #if defined(UNIX)
  33. #include <errno.h>
  34. #include <unistd.h>
  35. #include "osutil.h"
  36. #else
  37. #include <io.h>
  38. #endif
  39. #include <fcntl.h>
  40. #endif
  41. /***********************************************************************/
  42. /* Include application header files: */
  43. /* global.h is header containing all global declarations. */
  44. /* plgdbsem.h is header containing the DB application declarations. */
  45. /* tabdos.h is header containing the TABDOS class declarations. */
  46. /***********************************************************************/
  47. #include "global.h"
  48. #include "plgdbsem.h"
  49. #include "mycat.h"
  50. #include "filamap.h"
  51. #if defined(ZIP_SUPPORT)
  52. #include "filamzip.h"
  53. #endif // ZIP_SUPPORT
  54. #include "tabfmt.h"
  55. #include "tabmul.h"
  56. #define NO_FUNC
  57. #include "plgcnx.h" // For DB types
  58. #include "resource.h"
  59. /***********************************************************************/
  60. /* This should be an option. */
  61. /***********************************************************************/
  62. #define MAXCOL 200 /* Default max column nb in result */
  63. #define TYPE_UNKNOWN 10 /* Must be greater than other types */
  64. extern "C" int trace;
  65. /***********************************************************************/
  66. /* CSVColumns: constructs the result blocks containing the description */
  67. /* of all the columns of a CSV file that will be retrieved by #GetData.*/
  68. /* Note: the algorithm to set the type is based on the internal values */
  69. /* of types (TYPE_STRING < TYPE_DOUBLE < TYPE_INT) (1 < 2 < 7). */
  70. /* If these values are changed, this will have to be revisited. */
  71. /***********************************************************************/
  72. PQRYRES CSVColumns(PGLOBAL g, const char *fn, char sep, char q,
  73. int hdr, int mxr, bool info)
  74. {
  75. static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING,
  76. TYPE_INT, TYPE_INT, TYPE_SHORT};
  77. static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME,
  78. FLD_PREC, FLD_LENGTH, FLD_SCALE};
  79. static unsigned int length[] = {6, 6, 8, 10, 10, 6};
  80. char *p, *colname[MAXCOL], dechar, filename[_MAX_PATH], buf[4096];
  81. int i, imax, hmax, n, nerr, phase, blank, digit, dec, type;
  82. int ncol = sizeof(buftyp) / sizeof(int);
  83. int num_read = 0, num_max = 10000000; // Statistics
  84. int len[MAXCOL], typ[MAXCOL], prc[MAXCOL];
  85. FILE *infile;
  86. PQRYRES qrp;
  87. PCOLRES crp;
  88. if (info) {
  89. imax = hmax = 0;
  90. length[0] = 128;
  91. goto skipit;
  92. } // endif info
  93. // num_max = atoi(p+1); // Max num of record to test
  94. #if defined(WIN32)
  95. if (sep == ',' || strnicmp(setlocale(LC_NUMERIC, NULL), "French", 6))
  96. dechar = '.';
  97. else
  98. dechar = ',';
  99. #else // !WIN32
  100. dechar = '.';
  101. #endif // !WIN32
  102. if (trace)
  103. htrc("File %s sep=%c q=%c hdr=%d mxr=%d\n",
  104. SVP(fn), sep, q, hdr, mxr);
  105. if (!fn) {
  106. strcpy(g->Message, MSG(MISSING_FNAME));
  107. return NULL;
  108. } // endif fn
  109. imax = hmax = nerr = 0;
  110. mxr = max(0, mxr);
  111. for (i = 0; i < MAXCOL; i++) {
  112. colname[i] = NULL;
  113. len[i] = 0;
  114. typ[i] = TYPE_UNKNOWN;
  115. prc[i] = 0;
  116. } // endfor i
  117. /*********************************************************************/
  118. /* Open the input file. */
  119. /*********************************************************************/
  120. PlugSetPath(filename, fn, PlgGetDataPath(g));
  121. if (!(infile= global_fopen(g, MSGID_CANNOT_OPEN, filename, "r")))
  122. return NULL;
  123. if (hdr) {
  124. /*******************************************************************/
  125. /* Make the column names from the first line. */
  126. /*******************************************************************/
  127. phase = 0;
  128. if (fgets(buf, sizeof(buf), infile)) {
  129. n = strlen(buf) + 1;
  130. buf[n - 2] = '\0';
  131. #if defined(UNIX)
  132. // The file can be imported from Windows
  133. if (buf[n - 3] == '\r')
  134. buf[n - 3] = 0;
  135. #endif // UNIX
  136. p = (char*)PlugSubAlloc(g, NULL, n);
  137. memcpy(p, buf, n);
  138. //skip leading blanks
  139. for (; *p == ' '; p++) ;
  140. if (q && *p == q) {
  141. // Header is quoted
  142. p++;
  143. phase = 1;
  144. } // endif q
  145. colname[0] = p;
  146. } else {
  147. sprintf(g->Message, MSG(FILE_IS_EMPTY), fn);
  148. goto err;
  149. } // endif's
  150. for (i = 1; *p; p++)
  151. if (phase == 1 && *p == q) {
  152. *p = '\0';
  153. phase = 0;
  154. } else if (*p == sep && !phase) {
  155. *p = '\0';
  156. //skip leading blanks
  157. for (; *(p+1) == ' '; p++) ;
  158. if (q && *(p+1) == q) {
  159. // Header is quoted
  160. p++;
  161. phase = 1;
  162. } // endif q
  163. colname[i++] = p + 1;
  164. } // endif sep
  165. num_read++;
  166. imax = hmax = i;
  167. for (i = 0; i < hmax; i++)
  168. length[0] = max(length[0], strlen(colname[i]));
  169. } // endif hdr
  170. for (num_read++; num_read <= num_max; num_read++) {
  171. /*******************************************************************/
  172. /* Now start the reading process. Read one line. */
  173. /*******************************************************************/
  174. if (fgets(buf, sizeof(buf), infile)) {
  175. n = strlen(buf);
  176. buf[n - 1] = '\0';
  177. #if defined(UNIX)
  178. // The file can be imported from Windows
  179. if (buf[n - 2] == '\r')
  180. buf[n - 2] = 0;
  181. #endif // UNIX
  182. } else if (feof(infile)) {
  183. sprintf(g->Message, MSG(EOF_AFTER_LINE), num_read -1);
  184. break;
  185. } else {
  186. sprintf(g->Message, MSG(ERR_READING_REC), num_read, fn);
  187. goto err;
  188. } // endif's
  189. /*******************************************************************/
  190. /* Make the test for field lengths. */
  191. /*******************************************************************/
  192. i = n = phase = blank = digit = dec = 0;
  193. for (p = buf; *p; p++)
  194. if (*p == sep) {
  195. if (phase != 1) {
  196. if (i == MAXCOL - 1) {
  197. sprintf(g->Message, MSG(TOO_MANY_FIELDS), num_read, fn);
  198. goto err;
  199. } // endif i
  200. if (n) {
  201. len[i] = max(len[i], n);
  202. type = (digit || (dec && n == 1)) ? TYPE_STRING
  203. : (dec) ? TYPE_DOUBLE : TYPE_INT;
  204. typ[i] = min(type, typ[i]);
  205. prc[i] = max((typ[i] == TYPE_DOUBLE) ? (dec - 1) : 0, prc[i]);
  206. } // endif n
  207. i++;
  208. n = phase = blank = digit = dec = 0;
  209. } else // phase == 1
  210. n++;
  211. } else if (*p == ' ') {
  212. if (phase < 2)
  213. n++;
  214. if (blank)
  215. digit = 1;
  216. } else if (*p == q) {
  217. if (phase == 0) {
  218. if (blank)
  219. if (++nerr > mxr) {
  220. sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read);
  221. goto err;
  222. } else
  223. goto skip;
  224. n = 0;
  225. phase = digit = 1;
  226. } else if (phase == 1) {
  227. if (*(p+1) == q) {
  228. // This is currently not implemented for CSV tables
  229. // if (++nerr > mxr) {
  230. // sprintf(g->Message, MSG(QUOTE_IN_QUOTE), num_read);
  231. // goto err;
  232. // } else
  233. // goto skip;
  234. p++;
  235. n++;
  236. } else
  237. phase = 2;
  238. } else if (++nerr > mxr) { // phase == 2
  239. sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read);
  240. goto err;
  241. } else
  242. goto skip;
  243. } else {
  244. if (phase == 2)
  245. if (++nerr > mxr) {
  246. sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read);
  247. goto err;
  248. } else
  249. goto skip;
  250. // isdigit cannot be used here because of debug assert
  251. if (!strchr("0123456789", *p)) {
  252. if (!digit && *p == dechar)
  253. dec = 1; // Decimal point found
  254. else if (blank || !(*p == '-' || *p == '+'))
  255. digit = 1;
  256. } else if (dec)
  257. dec++; // More decimals
  258. n++;
  259. blank = 1;
  260. } // endif's *p
  261. if (phase == 1)
  262. if (++nerr > mxr) {
  263. sprintf(g->Message, MSG(UNBALANCE_QUOTE), num_read);
  264. goto err;
  265. } else
  266. goto skip;
  267. if (n) {
  268. len[i] = max(len[i], n);
  269. type = (digit || n == 0 || (dec && n == 1)) ? TYPE_STRING
  270. : (dec) ? TYPE_DOUBLE : TYPE_INT;
  271. typ[i] = min(type, typ[i]);
  272. prc[i] = max((typ[i] == TYPE_DOUBLE) ? (dec - 1) : 0, prc[i]);
  273. } // endif n
  274. imax = max(imax, i+1);
  275. skip: ; // Skip erroneous line
  276. } // endfor num_read
  277. if (trace) {
  278. htrc("imax=%d Lengths:", imax);
  279. for (i = 0; i < imax; i++)
  280. htrc(" %d", len[i]);
  281. htrc("\n");
  282. } // endif trace
  283. fclose(infile);
  284. skipit:
  285. if (trace)
  286. htrc("CSVColumns: imax=%d hmax=%d len=%d\n",
  287. imax, hmax, length[0]);
  288. /*********************************************************************/
  289. /* Allocate the structures used to refer to the result set. */
  290. /*********************************************************************/
  291. qrp = PlgAllocResult(g, ncol, imax, IDS_COLUMNS + 3,
  292. buftyp, fldtyp, length, false, false);
  293. if (info || !qrp)
  294. return qrp;
  295. qrp->Nblin = imax;
  296. /*********************************************************************/
  297. /* Now get the results into blocks. */
  298. /*********************************************************************/
  299. for (i = 0; i < imax; i++) {
  300. if (i >= hmax) {
  301. sprintf(buf, "COL%.3d", i+1);
  302. p = buf;
  303. } else
  304. p = colname[i];
  305. if (typ[i] == TYPE_UNKNOWN) // Void column
  306. typ[i] = TYPE_STRING;
  307. crp = qrp->Colresp; // Column Name
  308. crp->Kdata->SetValue(p, i);
  309. crp = crp->Next; // Data Type
  310. crp->Kdata->SetValue(typ[i], i);
  311. crp = crp->Next; // Type Name
  312. crp->Kdata->SetValue(GetTypeName(typ[i]), i);
  313. crp = crp->Next; // Precision
  314. crp->Kdata->SetValue(len[i], i);
  315. crp = crp->Next; // Length
  316. crp->Kdata->SetValue(len[i], i);
  317. crp = crp->Next; // Scale (precision)
  318. crp->Kdata->SetValue(prc[i], i);
  319. } // endfor i
  320. /*********************************************************************/
  321. /* Return the result pointer for use by GetData routines. */
  322. /*********************************************************************/
  323. return qrp;
  324. err:
  325. fclose(infile);
  326. return NULL;
  327. } // end of CSVCColumns
  328. /* --------------------------- Class CSVDEF -------------------------- */
  329. /***********************************************************************/
  330. /* CSVDEF constructor. */
  331. /***********************************************************************/
  332. CSVDEF::CSVDEF(void)
  333. {
  334. Fmtd = Accept = Header = false;
  335. Maxerr = 0;
  336. Quoted = -1;
  337. Sep = ',';
  338. Qot = '\0';
  339. } // end of CSVDEF constructor
  340. /***********************************************************************/
  341. /* DefineAM: define specific AM block values from XDB file. */
  342. /***********************************************************************/
  343. bool CSVDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
  344. {
  345. char buf[8];
  346. // Double check correctness of offset values
  347. if (Catfunc == FNC_NO)
  348. for (PCOLDEF cdp = To_Cols; cdp; cdp = cdp->GetNext())
  349. if (cdp->GetOffset() < 1) {
  350. strcpy(g->Message, MSG(BAD_OFFSET_VAL));
  351. return true;
  352. } // endif Offset
  353. // Call DOSDEF DefineAM with am=CSV so FMT is not confused with FIX
  354. if (DOSDEF::DefineAM(g, "CSV", poff))
  355. return true;
  356. Cat->GetCharCatInfo("Separator", ",", buf, sizeof(buf));
  357. Sep = (strlen(buf) == 2 && buf[0] == '\\' && buf[1] == 't') ? '\t' : *buf;
  358. Quoted = Cat->GetIntCatInfo("Quoted", -1);
  359. Cat->GetCharCatInfo("Qchar", "", buf, sizeof(buf));
  360. Qot = *buf;
  361. if (Qot && Quoted < 0)
  362. Quoted = 0;
  363. else if (!Qot && Quoted >= 0)
  364. Qot = '"';
  365. Fmtd = (!Sep || (am && (*am == 'F' || *am == 'f')));
  366. Header = (Cat->GetIntCatInfo("Header", 0) != 0);
  367. Maxerr = Cat->GetIntCatInfo("Maxerr", 0);
  368. Accept = (Cat->GetIntCatInfo("Accept", 0) != 0);
  369. return false;
  370. } // end of DefineAM
  371. /***********************************************************************/
  372. /* GetTable: makes a new Table Description Block. */
  373. /***********************************************************************/
  374. PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode)
  375. {
  376. PTDBASE tdbp;
  377. if (Catfunc != FNC_COL) {
  378. USETEMP tmp = PlgGetUser(g)->UseTemp;
  379. bool map = Mapped && mode != MODE_INSERT &&
  380. !(tmp != TMP_NO && mode == MODE_UPDATE) &&
  381. !(tmp == TMP_FORCE &&
  382. (mode == MODE_UPDATE || mode == MODE_DELETE));
  383. PTXF txfp;
  384. /*******************************************************************/
  385. /* Allocate a file processing class of the proper type. */
  386. /*******************************************************************/
  387. if (map) {
  388. // Should be now compatible with UNIX
  389. txfp = new(g) MAPFAM(this);
  390. } else if (Compressed) {
  391. #if defined(ZIP_SUPPORT)
  392. if (Compressed == 1)
  393. txfp = new(g) ZIPFAM(this);
  394. else {
  395. strcpy(g->Message, "Compress 2 not supported yet");
  396. return NULL;
  397. } // endelse
  398. #else // !ZIP_SUPPORT
  399. strcpy(g->Message, "Compress not supported");
  400. return NULL;
  401. #endif // !ZIP_SUPPORT
  402. } else
  403. txfp = new(g) DOSFAM(this);
  404. /*******************************************************************/
  405. /* Allocate a TDB of the proper type. */
  406. /* Column blocks will be allocated only when needed. */
  407. /*******************************************************************/
  408. if (!Fmtd)
  409. tdbp = new(g) TDBCSV(this, txfp);
  410. else
  411. tdbp = new(g) TDBFMT(this, txfp);
  412. if (Multiple)
  413. tdbp = new(g) TDBMUL(tdbp);
  414. } else
  415. tdbp = new(g)TDBCCL(this);
  416. return tdbp;
  417. } // end of GetTable
  418. /* -------------------------- Class TDBCSV --------------------------- */
  419. /***********************************************************************/
  420. /* Implementation of the TDBCSV class. */
  421. /***********************************************************************/
  422. TDBCSV::TDBCSV(PCSVDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
  423. {
  424. #if defined(_DEBUG)
  425. assert (tdp);
  426. #endif
  427. Field = NULL;
  428. Offset = NULL;
  429. Fldlen = NULL;
  430. Fields = 0;
  431. Nerr = 0;
  432. Quoted = tdp->Quoted;
  433. Maxerr = tdp->Maxerr;
  434. Accept = tdp->Accept;
  435. Header = tdp->Header;
  436. Sep = tdp->GetSep();
  437. Qot = tdp->GetQot();
  438. } // end of TDBCSV standard constructor
  439. TDBCSV::TDBCSV(PGLOBAL g, PTDBCSV tdbp) : TDBDOS(g, tdbp)
  440. {
  441. Fields = tdbp->Fields;
  442. if (Fields) {
  443. if (tdbp->Offset)
  444. Offset = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
  445. if (tdbp->Fldlen)
  446. Fldlen = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
  447. Field = (PSZ *)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields);
  448. for (int i = 0; i < Fields; i++) {
  449. if (Offset)
  450. Offset[i] = tdbp->Offset[i];
  451. if (Fldlen)
  452. Fldlen[i] = tdbp->Fldlen[i];
  453. if (Field) {
  454. assert (Fldlen);
  455. Field[i] = (PSZ)PlugSubAlloc(g, NULL, Fldlen[i] + 1);
  456. Field[i][Fldlen[i]] = '\0';
  457. } // endif Field
  458. } // endfor i
  459. } else {
  460. Field = NULL;
  461. Offset = NULL;
  462. Fldlen = NULL;
  463. } // endif Fields
  464. Nerr = tdbp->Nerr;
  465. Maxerr = tdbp->Maxerr;
  466. Quoted = tdbp->Quoted;
  467. Accept = tdbp->Accept;
  468. Header = tdbp->Header;
  469. Sep = tdbp->Sep;
  470. Qot = tdbp->Qot;
  471. } // end of TDBCSV copy constructor
  472. // Method
  473. PTDB TDBCSV::CopyOne(PTABS t)
  474. {
  475. PTDB tp;
  476. PCSVCOL cp1, cp2;
  477. PGLOBAL g = t->G; // Is this really useful ???
  478. tp = new(g) TDBCSV(g, this);
  479. for (cp1 = (PCSVCOL)Columns; cp1; cp1 = (PCSVCOL)cp1->GetNext()) {
  480. cp2 = new(g) CSVCOL(cp1, tp); // Make a copy
  481. NewPointer(t, cp1, cp2);
  482. } // endfor cp1
  483. return tp;
  484. } // end of CopyOne
  485. /***********************************************************************/
  486. /* Allocate CSV column description block. */
  487. /***********************************************************************/
  488. PCOL TDBCSV::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
  489. {
  490. return new(g) CSVCOL(g, cdp, this, cprec, n);
  491. } // end of MakeCol
  492. /***********************************************************************/
  493. /* Check whether the number of errors is greater than the maximum. */
  494. /***********************************************************************/
  495. bool TDBCSV::CheckErr(void)
  496. {
  497. return (++Nerr) > Maxerr;
  498. } // end of CheckErr
  499. /***********************************************************************/
  500. /* CSV EstimatedLength. Returns an estimated minimum line length. */
  501. /***********************************************************************/
  502. int TDBCSV::EstimatedLength(PGLOBAL g)
  503. {
  504. if (trace)
  505. htrc("EstimatedLength: Fields=%d Columns=%p\n", Fields, Columns);
  506. if (!Fields) {
  507. PCSVCOL colp;
  508. for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next)
  509. if (!colp->IsSpecial()) // Not a pseudo column
  510. Fields = max(Fields, (int)colp->Fldnum);
  511. if (Columns)
  512. Fields++; // Fldnum was 0 based
  513. } // endif Fields
  514. return (int)Fields; // Number of separators if all fields are null
  515. } // end of Estimated Length
  516. #if 0
  517. /***********************************************************************/
  518. /* CSV tables favor the use temporary files for Update. */
  519. /***********************************************************************/
  520. bool TDBCSV::IsUsingTemp(PGLOBAL g)
  521. {
  522. USETEMP usetemp = PlgGetUser(g)->UseTemp;
  523. return (usetemp == TMP_YES || usetemp == TMP_FORCE ||
  524. (usetemp == TMP_AUTO && Mode == MODE_UPDATE));
  525. } // end of IsUsingTemp
  526. #endif // 0 (Same as TDBDOS one)
  527. /***********************************************************************/
  528. /* CSV Access Method opening routine. */
  529. /* First allocate the Offset and Fldlen arrays according to the */
  530. /* greatest field used in that query. Then call the DOS opening fnc. */
  531. /***********************************************************************/
  532. bool TDBCSV::OpenDB(PGLOBAL g)
  533. {
  534. bool rc = false;
  535. PCOLDEF cdp;
  536. PDOSDEF tdp = (PDOSDEF)To_Def;
  537. if (Use != USE_OPEN && (Columns || Mode == MODE_UPDATE)) {
  538. // Allocate the storage used to read (or write) records
  539. int i, len;
  540. PCSVCOL colp;
  541. if (!Fields) // May have been set in TABFMT::OpenDB
  542. if (Mode != MODE_UPDATE && Mode != MODE_INSERT) {
  543. for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next)
  544. if (!colp->IsSpecial()) // Not a pseudo column
  545. Fields = max(Fields, (int)colp->Fldnum);
  546. if (Columns)
  547. Fields++; // Fldnum was 0 based
  548. } else
  549. for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
  550. Fields++;
  551. Offset = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
  552. Fldlen = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
  553. if (Mode == MODE_INSERT || Mode == MODE_UPDATE) {
  554. Field = (PSZ*)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields);
  555. Fldtyp = (bool*)PlugSubAlloc(g, NULL, sizeof(bool) * Fields);
  556. } // endif Mode
  557. for (i = 0; i < Fields; i++) {
  558. Offset[i] = 0;
  559. Fldlen[i] = 0;
  560. if (Field) {
  561. Field[i] = NULL;
  562. Fldtyp[i] = false;
  563. } // endif Field
  564. } // endfor i
  565. if (Field)
  566. // Prepare writing fields
  567. if (Mode != MODE_UPDATE)
  568. for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next) {
  569. i = colp->Fldnum;
  570. len = colp->GetLength();
  571. Field[i] = (PSZ)PlugSubAlloc(g, NULL, len + 1);
  572. Field[i][len] = '\0';
  573. Fldlen[i] = len;
  574. Fldtyp[i] = IsTypeNum(colp->GetResultType());
  575. } // endfor colp
  576. else // MODE_UPDATE
  577. for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext()) {
  578. i = cdp->GetOffset() - 1;
  579. len = cdp->GetLength();
  580. Field[i] = (PSZ)PlugSubAlloc(g, NULL, len + 1);
  581. Field[i][len] = '\0';
  582. Fldlen[i] = len;
  583. Fldtyp[i] = IsTypeNum(cdp->GetType());
  584. } // endfor colp
  585. } // endif Use
  586. if (Header) {
  587. // Check that the Lrecl is at least equal to the header line length
  588. int headlen = 0;
  589. PCOLDEF cdp;
  590. PDOSDEF tdp = (PDOSDEF)To_Def;
  591. for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
  592. headlen += strlen(cdp->GetName()) + 3; // 3 if names are quoted
  593. if (headlen > Lrecl) {
  594. Lrecl = headlen;
  595. Txfp->Lrecl = headlen;
  596. } // endif headlen
  597. } // endif Header
  598. Nerr = 0;
  599. rc = TDBDOS::OpenDB(g);
  600. if (!rc && Mode == MODE_UPDATE && To_Kindex)
  601. // Because KINDEX::Init is executed in mode READ, we must restore
  602. // the Fldlen array that was modified when reading the table file.
  603. for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
  604. Fldlen[cdp->GetOffset() - 1] = cdp->GetLength();
  605. return rc;
  606. } // end of OpenDB
  607. /***********************************************************************/
  608. /* SkipHeader: Physically skip first header line if applicable. */
  609. /* This is called from TDBDOS::OpenDB and must be executed before */
  610. /* Kindex construction if the file is accessed using an index. */
  611. /***********************************************************************/
  612. bool TDBCSV::SkipHeader(PGLOBAL g)
  613. {
  614. int len = GetFileLength(g);
  615. bool rc = false;
  616. #if defined(_DEBUG)
  617. if (len < 0)
  618. return true;
  619. #endif // _DEBUG
  620. if (Header) {
  621. if (Mode == MODE_INSERT) {
  622. if (!len) {
  623. // New file, the header line must be constructed and written
  624. int i, n = 0;
  625. int hlen = 0;
  626. bool q = Qot && Quoted > 0;
  627. PCOLDEF cdp;
  628. // Estimate the length of the header list
  629. for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext()) {
  630. hlen += (1 + strlen(cdp->GetName()));
  631. hlen += ((q) ? 2 : 0);
  632. n++; // Calculate the number of columns
  633. } // endfor cdp
  634. if (hlen > Lrecl) {
  635. sprintf(g->Message, MSG(LRECL_TOO_SMALL), hlen);
  636. return true;
  637. } // endif hlen
  638. // File is empty, write a header record
  639. memset(To_Line, 0, Lrecl);
  640. // The column order in the file is given by the offset value
  641. for (i = 1; i <= n; i++)
  642. for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext())
  643. if (cdp->GetOffset() == i) {
  644. if (q)
  645. To_Line[strlen(To_Line)] = Qot;
  646. strcat(To_Line, cdp->GetName());
  647. if (q)
  648. To_Line[strlen(To_Line)] = Qot;
  649. if (i < n)
  650. To_Line[strlen(To_Line)] = Sep;
  651. } // endif Offset
  652. rc = (Txfp->WriteBuffer(g) == RC_FX);
  653. } // endif !FileLength
  654. } else if (Mode == MODE_DELETE) {
  655. if (len)
  656. rc = (Txfp->SkipRecord(g, true) == RC_FX);
  657. } else if (len) // !Insert && !Delete
  658. rc = (Txfp->SkipRecord(g, false) == RC_FX || Txfp->RecordPos(g));
  659. } // endif Header
  660. return rc;
  661. } // end of SkipHeader
  662. /***********************************************************************/
  663. /* ReadBuffer: Physical read routine for the CSV access method. */
  664. /***********************************************************************/
  665. int TDBCSV::ReadBuffer(PGLOBAL g)
  666. {
  667. char *p1, *p2, *p = NULL;
  668. int i, n, len, rc = Txfp->ReadBuffer(g);
  669. bool bad = false;
  670. if (trace > 1)
  671. htrc("CSV: Row is '%s' rc=%d\n", To_Line, rc);
  672. if (rc != RC_OK || !Fields)
  673. return rc;
  674. else
  675. p2 = To_Line;
  676. // Find the offsets and lengths of the columns for this row
  677. for (i = 0; i < Fields; i++) {
  678. if (!bad) {
  679. if (Qot && *p2 == Qot) { // Quoted field
  680. for (n = 0, p1 = ++p2; (p = strchr(p1, Qot)); p1 = p + 2)
  681. if (*(p + 1) == Qot)
  682. n++; // Doubled internal quotes
  683. else
  684. break; // Final quote
  685. if (p) {
  686. len = p++ - p2;
  687. // if (Sep != ' ')
  688. // for (; *p == ' '; p++) ; // Skip blanks
  689. if (*p != Sep && i != Fields - 1) { // Should be the separator
  690. if (CheckErr()) {
  691. sprintf(g->Message, MSG(MISSING_FIELD),
  692. i+1, Name, RowNumber(g));
  693. return RC_FX;
  694. } else if (Accept)
  695. bad = true;
  696. else
  697. return RC_NF;
  698. } // endif p
  699. if (n) {
  700. int j, k;
  701. // Suppress the double of internal quotes
  702. for (j = k = 0; j < len; j++, k++) {
  703. if (p2[j] == Qot)
  704. j++; // skip first one
  705. p2[k] = p2[j];
  706. } // endfor i, j
  707. len -= n;
  708. } // endif n
  709. } else if (CheckErr()) {
  710. sprintf(g->Message, MSG(BAD_QUOTE_FIELD),
  711. Name, i+1, RowNumber(g));
  712. return RC_FX;
  713. } else if (Accept) {
  714. len = strlen(p2);
  715. bad = true;
  716. } else
  717. return RC_NF;
  718. } else if ((p = strchr(p2, Sep)))
  719. len = p - p2;
  720. else if (i == Fields - 1)
  721. len = strlen(p2);
  722. else if (Accept && Maxerr == 0) {
  723. len = strlen(p2);
  724. bad = true;
  725. } else if (CheckErr()) {
  726. sprintf(g->Message, MSG(MISSING_FIELD), i+1, Name, RowNumber(g));
  727. return RC_FX;
  728. } else if (Accept) {
  729. len = strlen(p2);
  730. bad = true;
  731. } else
  732. return RC_NF;
  733. } else
  734. len = 0;
  735. Offset[i] = p2 - To_Line;
  736. if (Mode != MODE_UPDATE)
  737. Fldlen[i] = len;
  738. else if (len > Fldlen[i]) {
  739. sprintf(g->Message, MSG(FIELD_TOO_LONG), i+1, RowNumber(g));
  740. return RC_FX;
  741. } else {
  742. strncpy(Field[i], p2, len);
  743. Field[i][len] = '\0';
  744. } // endif Mode
  745. if (p)
  746. p2 = p + 1;
  747. } // endfor i
  748. return rc;
  749. } // end of ReadBuffer
  750. /***********************************************************************/
  751. /* Data Base write routine CSV file access method. */
  752. /***********************************************************************/
  753. int TDBCSV::WriteDB(PGLOBAL g)
  754. {
  755. char sep[2], qot[2];
  756. int i, nlen, oldlen = strlen(To_Line);
  757. if (trace > 1)
  758. htrc("CSV WriteDB: R%d Mode=%d key=%p link=%p\n",
  759. Tdb_No, Mode, To_Key_Col, To_Link);
  760. // Before writing the line we must check its length
  761. if ((nlen = CheckWrite(g)) < 0)
  762. return RC_FX;
  763. // Before writing the line we must make it
  764. sep[0] = Sep;
  765. sep[1] = '\0';
  766. qot[0] = Qot;
  767. qot[1] = '\0';
  768. *To_Line = '\0';
  769. for (i = 0; i < Fields; i++) {
  770. if (i)
  771. strcat(To_Line, sep);
  772. if (Field[i])
  773. if (!strlen(Field[i])) {
  774. // Generally null fields are not quoted
  775. if (Quoted > 2)
  776. // Except if explicitely required
  777. strcat(strcat(To_Line, qot), qot);
  778. } else if (Qot && (strchr(Field[i], Sep) || *Field[i] == Qot
  779. || Quoted > 1 || (Quoted == 1 && !Fldtyp[i])))
  780. if (strchr(Field[i], Qot)) {
  781. // Field contains quotes that must be doubled
  782. int j, k = strlen(To_Line), n = strlen(Field[i]);
  783. To_Line[k++] = Qot;
  784. for (j = 0; j < n; j++) {
  785. if (Field[i][j] == Qot)
  786. To_Line[k++] = Qot;
  787. To_Line[k++] = Field[i][j];
  788. } // endfor j
  789. To_Line[k++] = Qot;
  790. To_Line[k] = '\0';
  791. } else
  792. strcat(strcat(strcat(To_Line, qot), Field[i]), qot);
  793. else
  794. strcat(To_Line, Field[i]);
  795. } // endfor i
  796. #if defined(_DEBUG)
  797. assert ((unsigned)nlen == strlen(To_Line));
  798. #endif
  799. if (Mode == MODE_UPDATE && nlen < oldlen
  800. && !((PDOSFAM)Txfp)->GetUseTemp()) {
  801. // In Update mode with no temp file, line length must not change
  802. To_Line[nlen] = Sep;
  803. for (nlen++; nlen < oldlen; nlen++)
  804. To_Line[nlen] = ' ';
  805. To_Line[nlen] = '\0';
  806. } // endif
  807. if (trace > 1)
  808. htrc("Write: line is=%s", To_Line);
  809. /*********************************************************************/
  810. /* Now start the writing process. */
  811. /*********************************************************************/
  812. return Txfp->WriteBuffer(g);
  813. } // end of WriteDB
  814. /***********************************************************************/
  815. /* Check whether a new line fit in the file lrecl size. */
  816. /***********************************************************************/
  817. int TDBCSV::CheckWrite(PGLOBAL g)
  818. {
  819. int maxlen, n, nlen = (Fields - 1);
  820. if (trace > 1)
  821. htrc("CheckWrite: R%d Mode=%d\n", Tdb_No, Mode);
  822. // Before writing the line we must check its length
  823. maxlen = (Mode == MODE_UPDATE && !Txfp->GetUseTemp())
  824. ? strlen(To_Line) : Lrecl;
  825. // Check whether record is too int
  826. for (int i = 0; i < Fields; i++)
  827. if (Field[i]) {
  828. if (!(n = strlen(Field[i])))
  829. n += (Quoted > 2 ? 2 : 0);
  830. else if (strchr(Field[i], Sep) || (Qot && *Field[i] == Qot)
  831. || Quoted > 1 || (Quoted == 1 && !Fldtyp[i]))
  832. if (!Qot) {
  833. sprintf(g->Message, MSG(SEP_IN_FIELD), i + 1);
  834. return -1;
  835. } else {
  836. // Quotes inside a quoted field must be doubled
  837. char *p1, *p2;
  838. for (p1 = Field[i]; (p2 = strchr(p1, Qot)); p1 = p2 + 1)
  839. n++;
  840. n += 2; // Outside quotes
  841. } // endif
  842. if ((nlen += n) > maxlen) {
  843. strcpy(g->Message, MSG(LINE_TOO_LONG));
  844. return -1;
  845. } // endif nlen
  846. } // endif Field
  847. return nlen;
  848. } // end of CheckWrite
  849. /* ------------------------------------------------------------------- */
  850. /***********************************************************************/
  851. /* Implementation of the TDBFMT class. */
  852. /***********************************************************************/
  853. TDBFMT::TDBFMT(PGLOBAL g, PTDBFMT tdbp) : TDBCSV(g, tdbp)
  854. {
  855. FldFormat = tdbp->FldFormat;
  856. To_Fld = tdbp->To_Fld;
  857. FmtTest = tdbp->FmtTest;
  858. Linenum = tdbp->Linenum;
  859. } // end of TDBFMT copy constructor
  860. // Method
  861. PTDB TDBFMT::CopyOne(PTABS t)
  862. {
  863. PTDB tp;
  864. PCSVCOL cp1, cp2;
  865. //PFMTCOL cp1, cp2;
  866. PGLOBAL g = t->G; // Is this really useful ???
  867. tp = new(g) TDBFMT(g, this);
  868. for (cp1 = (PCSVCOL)Columns; cp1; cp1 = (PCSVCOL)cp1->GetNext()) {
  869. //for (cp1 = (PFMTCOL)Columns; cp1; cp1 = (PFMTCOL)cp1->GetNext()) {
  870. cp2 = new(g) CSVCOL(cp1, tp); // Make a copy
  871. // cp2 = new(g) FMTCOL(cp1, tp); // Make a copy
  872. NewPointer(t, cp1, cp2);
  873. } // endfor cp1
  874. return tp;
  875. } // end of CopyOne
  876. /***********************************************************************/
  877. /* Allocate FMT column description block. */
  878. /***********************************************************************/
  879. PCOL TDBFMT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
  880. {
  881. return new(g) CSVCOL(g, cdp, this, cprec, n);
  882. //return new(g) FMTCOL(cdp, this, cprec, n);
  883. } // end of MakeCol
  884. /***********************************************************************/
  885. /* FMT EstimatedLength. Returns an estimated minimum line length. */
  886. /* The big problem here is how can we astimated that minimum ? */
  887. /***********************************************************************/
  888. int TDBFMT::EstimatedLength(PGLOBAL g)
  889. {
  890. // This is rather stupid !!!
  891. return ((PDOSDEF)To_Def)->GetEnding() + (int)((Lrecl / 10) + 1);
  892. } // end of EstimatedLength
  893. /***********************************************************************/
  894. /* FMT Access Method opening routine. */
  895. /***********************************************************************/
  896. bool TDBFMT::OpenDB(PGLOBAL g)
  897. {
  898. Linenum = 0;
  899. if (Mode == MODE_INSERT || Mode == MODE_UPDATE) {
  900. sprintf(g->Message, MSG(FMT_WRITE_NIY), "FMT");
  901. return true; // NIY
  902. } // endif Mode
  903. if (Use != USE_OPEN && Columns) {
  904. // Make the formats used to read records
  905. PSZ pfm;
  906. int i, n;
  907. PCSVCOL colp;
  908. PCOLDEF cdp;
  909. PDOSDEF tdp = (PDOSDEF)To_Def;
  910. for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next)
  911. if (!colp->IsSpecial()) // Not a pseudo column
  912. Fields = max(Fields, (int)colp->Fldnum);
  913. if (Columns)
  914. Fields++; // Fldnum was 0 based
  915. To_Fld = PlugSubAlloc(g, NULL, Lrecl + 1);
  916. FldFormat = (PSZ*)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields);
  917. memset(FldFormat, 0, sizeof(PSZ) * Fields);
  918. FmtTest = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields);
  919. memset(FmtTest, 0, sizeof(int) * Fields);
  920. // Get the column formats
  921. for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext())
  922. if ((i = cdp->GetOffset() - 1) < Fields) {
  923. if (!(pfm = cdp->GetFmt())) {
  924. sprintf(g->Message, MSG(NO_FLD_FORMAT), i + 1, Name);
  925. return true;
  926. } // endif pfm
  927. // Roughly check the Fmt format
  928. if ((n = strlen(pfm) - 2) < 4) {
  929. sprintf(g->Message, MSG(BAD_FLD_FORMAT), i + 1, Name);
  930. return true;
  931. } // endif n
  932. FldFormat[i] = (PSZ)PlugSubAlloc(g, NULL, n + 5);
  933. strcpy(FldFormat[i], pfm);
  934. if (!strcmp(pfm + n, "%m")) {
  935. // This is a field that can be missing. Flag it so it can
  936. // be handled with special processing.
  937. FldFormat[i][n+1] = 'n'; // To have sscanf normal processing
  938. FmtTest[i] = 2;
  939. } else if (i+1 < Fields && strcmp(pfm + n, "%n")) {
  940. // There are trailing characters after the field contents
  941. // add a marker for the next field start position.
  942. strcat(FldFormat[i], "%n");
  943. FmtTest[i] = 1;
  944. } // endif's
  945. } // endif i
  946. } // endif Use
  947. return TDBCSV::OpenDB(g);
  948. } // end of OpenDB
  949. /***********************************************************************/
  950. /* ReadBuffer: Physical read routine for the FMT access method. */
  951. /***********************************************************************/
  952. int TDBFMT::ReadBuffer(PGLOBAL g)
  953. {
  954. int i, len, n, deb, fin, nwp, pos = 0, rc;
  955. bool bad = false;
  956. if ((rc = Txfp->ReadBuffer(g)) != RC_OK || !Fields)
  957. return rc;
  958. else
  959. ++Linenum;
  960. if (trace > 1)
  961. htrc("FMT: Row %d is '%s' rc=%d\n", Linenum, To_Line, rc);
  962. // Find the offsets and lengths of the columns for this row
  963. for (i = 0; i < Fields; i++) {
  964. if (!bad) {
  965. deb = fin = -1;
  966. if (!FldFormat[i]) {
  967. n = 0;
  968. } else if (FmtTest[i] == 1) {
  969. nwp = -1;
  970. n = sscanf(To_Line + pos, FldFormat[i], &deb, To_Fld, &fin, &nwp);
  971. } else {
  972. n = sscanf(To_Line + pos, FldFormat[i], &deb, To_Fld, &fin);
  973. if (n != 1 && (deb >= 0 || i == Fields - 1) && FmtTest[i] == 2) {
  974. // Missing optional field, not an error
  975. n = 1;
  976. if (i == Fields - 1)
  977. fin = deb = 0;
  978. else
  979. fin = deb;
  980. } // endif n
  981. nwp = fin;
  982. } // endif i
  983. if (n != 1 || deb < 0 || fin < 0 || nwp < 0) {
  984. // This is to avoid a very strange sscanf bug occuring
  985. // with fields that ends with a null character.
  986. // This bug causes subsequent sscanf to return in error,
  987. // so next lines are not parsed correctly.
  988. sscanf("a", "%*c"); // Seems to reset things Ok
  989. if (CheckErr()) {
  990. sprintf(g->Message, MSG(BAD_LINEFLD_FMT), Linenum, i + 1, Name);
  991. return RC_FX;
  992. } else if (Accept)
  993. bad = true;
  994. else
  995. return RC_NF;
  996. } // endif n...
  997. } // endif !bad
  998. if (!bad) {
  999. Offset[i] = pos + deb;
  1000. len = fin - deb;
  1001. } else {
  1002. nwp = 0;
  1003. Offset[i] = pos;
  1004. len = 0;
  1005. } // endif bad
  1006. // if (Mode != MODE_UPDATE)
  1007. Fldlen[i] = len;
  1008. // else if (len > Fldlen[i]) {
  1009. // sprintf(g->Message, MSG(FIELD_TOO_LONG), i+1, To_Tdb->RowNumber(g));
  1010. // return RC_FX;
  1011. // } else {
  1012. // strncpy(Field[i], To_Line + pos, len);
  1013. // Field[i][len] = '\0';
  1014. // } // endif Mode
  1015. pos += nwp;
  1016. } // endfor i
  1017. if (bad)
  1018. Nerr++;
  1019. else
  1020. sscanf("a", "%*c"); // Seems to reset things Ok
  1021. return rc;
  1022. } // end of ReadBuffer
  1023. /***********************************************************************/
  1024. /* Data Base write routine FMT file access method. */
  1025. /***********************************************************************/
  1026. int TDBFMT::WriteDB(PGLOBAL g)
  1027. {
  1028. sprintf(g->Message, MSG(FMT_WRITE_NIY), "FMT");
  1029. return RC_FX; // NIY
  1030. } // end of WriteDB
  1031. // ------------------------ CSVCOL functions ----------------------------
  1032. /***********************************************************************/
  1033. /* CSVCOL public constructor */
  1034. /***********************************************************************/
  1035. CSVCOL::CSVCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
  1036. : DOSCOL(g, cdp, tdbp, cprec, i, "CSV")
  1037. {
  1038. Fldnum = Deplac - 1;
  1039. Deplac = 0;
  1040. } // end of CSVCOL constructor
  1041. /***********************************************************************/
  1042. /* CSVCOL constructor used for copying columns. */
  1043. /* tdbp is the pointer to the new table descriptor. */
  1044. /***********************************************************************/
  1045. CSVCOL::CSVCOL(CSVCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp)
  1046. {
  1047. Fldnum = col1->Fldnum;
  1048. } // end of CSVCOL copy constructor
  1049. /***********************************************************************/
  1050. /* ReadColumn: call DOSCOL::ReadColumn after having set the offet */
  1051. /* and length of the field to read as calculated by TDBCSV::ReadDB. */
  1052. /***********************************************************************/
  1053. void CSVCOL::ReadColumn(PGLOBAL g)
  1054. {
  1055. int rc;
  1056. PTDBCSV tdbp = (PTDBCSV)To_Tdb;
  1057. /*********************************************************************/
  1058. /* If physical reading of the line was deferred, do it now. */
  1059. /*********************************************************************/
  1060. if (!tdbp->IsRead())
  1061. if ((rc = tdbp->ReadBuffer(g)) != RC_OK) {
  1062. if (rc == RC_EF)
  1063. sprintf(g->Message, MSG(INV_DEF_READ), rc);
  1064. longjmp(g->jumper[g->jump_level], 34);
  1065. } // endif
  1066. if (tdbp->Mode != MODE_UPDATE) {
  1067. int colen = Long; // Column length
  1068. // Set the field offset and length for this row
  1069. Deplac = tdbp->Offset[Fldnum]; // Field offset
  1070. Long = tdbp->Fldlen[Fldnum]; // Field length
  1071. if (trace > 1)
  1072. htrc("CSV ReadColumn %s Fldnum=%d offset=%d fldlen=%d\n",
  1073. Name, Fldnum, Deplac, Long);
  1074. if (Long > colen && tdbp->CheckErr()) {
  1075. Long = colen; // Restore column length
  1076. sprintf(g->Message, MSG(FLD_TOO_LNG_FOR),
  1077. Fldnum + 1, Name, To_Tdb->RowNumber(g), tdbp->GetFile(g));
  1078. longjmp(g->jumper[g->jump_level], 34);
  1079. } // endif Long
  1080. // Now do the reading
  1081. DOSCOL::ReadColumn(g);
  1082. // Restore column length
  1083. Long = colen;
  1084. } else { // Mode Update
  1085. // Field have been copied in TDB Field array
  1086. PSZ fp = tdbp->Field[Fldnum];
  1087. Value->SetValue_psz(fp);
  1088. // Set null when applicable
  1089. if (Nullable)
  1090. Value->SetNull(Value->IsZero());
  1091. } // endif Mode
  1092. } // end of ReadColumn
  1093. /***********************************************************************/
  1094. /* WriteColumn: The column is written in TDBCSV matching Field. */
  1095. /***********************************************************************/
  1096. void CSVCOL::WriteColumn(PGLOBAL g)
  1097. {
  1098. char *p, buf[32];
  1099. int flen;
  1100. PTDBCSV tdbp = (PTDBCSV)To_Tdb;
  1101. if (trace > 1)
  1102. htrc("CSV WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
  1103. Name, tdbp->GetTdb_No(), ColUse, Status);
  1104. flen = GetLength();
  1105. if (trace > 1)
  1106. htrc("Lrecl=%d Long=%d field=%d coltype=%d colval=%p\n",
  1107. tdbp->Lrecl, Long, flen, Buf_Type, Value);
  1108. /*********************************************************************/
  1109. /* Check whether the new value has to be converted to Buf_Type. */
  1110. /*********************************************************************/
  1111. if (Value != To_Val)
  1112. Value->SetValue_pval(To_Val, false); // Convert the updated value
  1113. /*********************************************************************/
  1114. /* Get the string representation of the column value. */
  1115. /*********************************************************************/
  1116. p = Value->ShowValue(buf);
  1117. if (trace > 1)
  1118. htrc("new length(%p)=%d\n", p, strlen(p));
  1119. if ((signed)strlen(p) > flen) {
  1120. sprintf(g->Message, MSG(BAD_FLD_LENGTH), Name, p, flen,
  1121. tdbp->RowNumber(g), tdbp->GetFile(g));
  1122. longjmp(g->jumper[g->jump_level], 34);
  1123. } // endif
  1124. if (trace > 1)
  1125. htrc("buffer=%s\n", p);
  1126. /*********************************************************************/
  1127. /* Updating must be done also during the first pass so writing the */
  1128. /* updated record can be checked for acceptable record length. */
  1129. /*********************************************************************/
  1130. if (Fldnum < 0) {
  1131. // This can happen for wrong offset value in XDB files
  1132. sprintf(g->Message, MSG(BAD_FIELD_RANK), Fldnum + 1, Name);
  1133. longjmp(g->jumper[g->jump_level], 34);
  1134. } else
  1135. strncpy(tdbp->Field[Fldnum], p, flen);
  1136. if (trace > 1)
  1137. htrc(" col written: '%s'\n", p);
  1138. } // end of WriteColumn
  1139. /* ---------------------------TDBCCL class --------------------------- */
  1140. /***********************************************************************/
  1141. /* TDBCCL class constructor. */
  1142. /***********************************************************************/
  1143. TDBCCL::TDBCCL(PCSVDEF tdp) : TDBCAT(tdp)
  1144. {
  1145. Fn = tdp->GetFn();
  1146. Hdr = tdp->Header;
  1147. Mxr = tdp->Maxerr;
  1148. Qtd = tdp->Quoted;
  1149. Sep = tdp->Sep;
  1150. } // end of TDBCCL constructor
  1151. /***********************************************************************/
  1152. /* GetResult: Get the list the CSV file columns. */
  1153. /***********************************************************************/
  1154. PQRYRES TDBCCL::GetResult(PGLOBAL g)
  1155. {
  1156. return CSVColumns(g, Fn, Sep, Qtd, Hdr, Mxr, false);
  1157. } // end of GetResult
  1158. /* ------------------------ End of TabFmt ---------------------------- */