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.

1683 lines
54 KiB

  1. /********** PlgDBUtl Fpe C++ Program Source Code File (.CPP) ***********/
  2. /* PROGRAM NAME: PLGDBUTL */
  3. /* ------------- */
  4. /* Version 3.8 */
  5. /* */
  6. /* COPYRIGHT: */
  7. /* ---------- */
  8. /* (C) Copyright to the author Olivier BERTRAND 1998-2013 */
  9. /* */
  10. /* WHAT THIS PROGRAM DOES: */
  11. /* ----------------------- */
  12. /* Utility functions used by DB semantic routines. */
  13. /* */
  14. /* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
  15. /* -------------------------------------- */
  16. /* */
  17. /* REQUIRED FILES: */
  18. /* --------------- */
  19. /* See Readme.C for a list and description of required SYSTEM files. */
  20. /* */
  21. /* PLGDBUTL.C - Source code */
  22. /* GLOBAL.H - Global declaration file */
  23. /* PLGDBSEM.H - DB application declaration file */
  24. /* */
  25. /* REQUIRED LIBRARIES: */
  26. /* ------------------- */
  27. /* OS2.LIB - OS2 libray */
  28. /* LLIBCE.LIB - Protect mode/standard combined large model C */
  29. /* library */
  30. /* */
  31. /* REQUIRED PROGRAMS: */
  32. /* ------------------ */
  33. /* IBM, MS, Borland or GNU C++ Compiler */
  34. /* IBM, MS, Borland or GNU Linker */
  35. /***********************************************************************/
  36. /***********************************************************************/
  37. /* Include relevant MariaDB header file. */
  38. /***********************************************************************/
  39. #include "my_global.h"
  40. #if defined(WIN32)
  41. #include <io.h>
  42. #include <fcntl.h>
  43. #include <errno.h>
  44. #define BIGMEM 1048576 // 1 Megabyte
  45. #else // !WIN32
  46. #include <unistd.h>
  47. #include <fcntl.h>
  48. #if defined(THREAD)
  49. #include <pthread.h>
  50. #endif // THREAD
  51. #include <stdarg.h>
  52. #define BIGMEM 2147483647 // Max int value
  53. #endif // !WIN32
  54. #include <locale.h>
  55. /***********************************************************************/
  56. /* Include application header files */
  57. /***********************************************************************/
  58. #include "global.h" // header containing all global declarations.
  59. #include "plgdbsem.h" // header containing the DB applic. declarations.
  60. #include "preparse.h" // For DATPAR
  61. #include "osutil.h"
  62. #include "maputil.h"
  63. #include "catalog.h"
  64. #include "colblk.h"
  65. #include "xtable.h" // header of TBX, TDB and TDBASE classes
  66. #include "tabcol.h" // header of XTAB and COLUMN classes
  67. #include "valblk.h"
  68. #include "rcmsg.h"
  69. /***********************************************************************/
  70. /* Macro or external routine definition */
  71. /***********************************************************************/
  72. #if defined(THREAD)
  73. #if defined(WIN32)
  74. extern CRITICAL_SECTION parsec; // Used calling the Flex parser
  75. #else // !WIN32
  76. extern pthread_mutex_t parmut;
  77. #endif // !WIN32
  78. #endif // THREAD
  79. #define PLGINI "plugdb.ini" /* Configuration settings file */
  80. #define PLGXINI "plgcnx.ini" /* Configuration settings file */
  81. /***********************************************************************/
  82. /* DB static variables. */
  83. /***********************************************************************/
  84. bool Initdone = false;
  85. bool plugin = false; // True when called by the XDB plugin handler
  86. extern "C" {
  87. char plgxini[_MAX_PATH] = PLGXINI;
  88. char plgini[_MAX_PATH] = PLGINI;
  89. #if defined(WIN32)
  90. char nmfile[_MAX_PATH] = ".\\Log\\plugdb.out";
  91. char pdebug[_MAX_PATH] = ".\\Log\\plgthread.out";
  92. HINSTANCE s_hModule; // Saved module handle
  93. #else // !WIN32
  94. char nmfile[_MAX_PATH] = "./Log/plugdb.out";
  95. char pdebug[_MAX_PATH] = "./Log/plgthread.out";
  96. #endif // !WIN32
  97. #if defined(XMSG)
  98. char msglang[16] = "ENGLISH"; // Default language
  99. #endif
  100. } // extern "C"
  101. extern "C" int trace;
  102. extern "C" char version[];
  103. // The debug trace used by the main thread
  104. FILE *pfile = NULL;
  105. MBLOCK Nmblk = {NULL, false, 0, false, NULL}; // Used to init MBLOCK's
  106. /***********************************************************************/
  107. /* Routines called externally and internally by utility routines. */
  108. /***********************************************************************/
  109. bool PlugEvalLike(PGLOBAL, LPCSTR, LPCSTR, bool);
  110. bool EvalLikePattern(LPCSTR, LPCSTR);
  111. void PlugConvertConstant(PGLOBAL, void* &, short&);
  112. #ifdef DOMDOC_SUPPORT
  113. void CloseXMLFile(PGLOBAL, PFBLOCK, bool);
  114. #endif // DOMDOC_SUPPORT
  115. #ifdef LIBXML2_SUPPORT
  116. #include "libdoc.h"
  117. #endif // LIBXML2_SUPPORT
  118. /***********************************************************************/
  119. /* Routines for file IO with error reporting to g->Message */
  120. /***********************************************************************/
  121. static void
  122. global_open_error_msg(GLOBAL *g, int msgid, const char *path, const char *mode)
  123. {
  124. int len;
  125. switch (msgid)
  126. {
  127. case MSGID_CANNOT_OPEN:
  128. len= snprintf(g->Message, sizeof(g->Message) - 1,
  129. MSG(CANNOT_OPEN), // Cannot open %s
  130. path);
  131. break;
  132. case MSGID_OPEN_MODE_ERROR:
  133. len= snprintf(g->Message, sizeof(g->Message) - 1,
  134. MSG(OPEN_MODE_ERROR), // "Open(%s) error %d on %s"
  135. mode, (int) errno, path);
  136. break;
  137. case MSGID_OPEN_MODE_STRERROR:
  138. len= snprintf(g->Message, sizeof(g->Message) - 1,
  139. MSG(OPEN_MODE_ERROR) ": %s", // Open(%s) error %d on %s: %s
  140. mode, (int) errno, path, strerror(errno));
  141. break;
  142. case MSGID_OPEN_STRERROR:
  143. len= snprintf(g->Message, sizeof(g->Message) - 1,
  144. MSG(OPEN_STRERROR), // "open error: %s"
  145. strerror(errno));
  146. break;
  147. case MSGID_OPEN_ERROR_AND_STRERROR:
  148. len= snprintf(g->Message, sizeof(g->Message) - 1,
  149. //OPEN_ERROR does not work, as it wants mode %d (not %s)
  150. //MSG(OPEN_ERROR) "%s",// "Open error %d in mode %d on %s: %s"
  151. "Open error %d in mode %s on %s: %s",
  152. errno, mode, path, strerror(errno));
  153. break;
  154. case MSGID_OPEN_EMPTY_FILE:
  155. len= snprintf(g->Message, sizeof(g->Message) - 1,
  156. MSG(OPEN_EMPTY_FILE), // "Opening empty file %s: %s"
  157. path, strerror(errno));
  158. break;
  159. default:
  160. DBUG_ASSERT(0);
  161. /* Fall through*/
  162. case 0:
  163. len= 0;
  164. }
  165. g->Message[len]= '\0';
  166. }
  167. FILE *global_fopen(GLOBAL *g, int msgid, const char *path, const char *mode)
  168. {
  169. FILE *f;
  170. if (!(f= fopen(path, mode)))
  171. global_open_error_msg(g, msgid, path, mode);
  172. return f;
  173. }
  174. int global_open(GLOBAL *g, int msgid, const char *path, int flags)
  175. {
  176. int h;
  177. if ((h= open(path, flags)) <= 0)
  178. global_open_error_msg(g, msgid, path, "");
  179. return h;
  180. }
  181. int global_open(GLOBAL *g, int msgid, const char *path, int flags, int mode)
  182. {
  183. int h;
  184. if ((h= open(path, flags, mode)) <= 0)
  185. {
  186. char modestr[64];
  187. snprintf(modestr, sizeof(modestr), "%d", mode);
  188. global_open_error_msg(g, msgid, path, modestr);
  189. }
  190. return h;
  191. }
  192. /**************************************************************************/
  193. /* Utility for external callers (such as XDB) */
  194. /**************************************************************************/
  195. DllExport char *GetIni(int n)
  196. {
  197. switch (n) {
  198. case 1: return plgxini; break;
  199. case 2: return nmfile; break;
  200. case 3: return pdebug; break;
  201. case 4: return version; break;
  202. #if defined(XMSG)
  203. case 5: return msglang; break;
  204. #endif // XMSG
  205. // default: return plgini;
  206. } // endswitch GetIni
  207. return plgini;
  208. } // end of GetIni
  209. DllExport void SetTrc(void)
  210. {
  211. // If tracing is on, debug must be initialized.
  212. debug = pfile;
  213. } // end of SetTrc
  214. #if 0
  215. /**************************************************************************/
  216. /* Tracing output function. */
  217. /**************************************************************************/
  218. void ptrc(char const *fmt, ...)
  219. {
  220. va_list ap;
  221. va_start (ap, fmt);
  222. // if (trace == 0 || (trace == 1 && !pfile) || !fmt)
  223. // printf("In %s wrong trace=%d pfile=%p fmt=%p\n",
  224. // __FILE__, trace, pfile, fmt);
  225. if (trace == 1)
  226. vfprintf(pfile, fmt, ap);
  227. else
  228. vprintf(fmt, ap);
  229. va_end (ap);
  230. } // end of ptrc
  231. #endif // 0
  232. /**************************************************************************/
  233. /* Allocate the result structure that will contain result data. */
  234. /**************************************************************************/
  235. PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids,
  236. int *buftyp, XFLD *fldtyp,
  237. unsigned int *length, bool blank, bool nonull)
  238. {
  239. char cname[NAM_LEN+1];
  240. int i;
  241. PCOLRES *pcrp, crp;
  242. PQRYRES qrp;
  243. /************************************************************************/
  244. /* Allocate the structure used to contain the result set. */
  245. /************************************************************************/
  246. qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES));
  247. pcrp = &qrp->Colresp;
  248. qrp->Continued = false;
  249. qrp->Truncated = false;
  250. qrp->Info = false;
  251. qrp->Suball = true;
  252. qrp->Maxres = maxres;
  253. qrp->Maxsize = 0;
  254. qrp->Nblin = 0;
  255. qrp->Nbcol = 0; // will be ncol
  256. qrp->Cursor = 0;
  257. qrp->BadLines = 0;
  258. for (i = 0; i < ncol; i++) {
  259. *pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
  260. crp = *pcrp;
  261. pcrp = &crp->Next;
  262. memset(crp, 0, sizeof(COLRES));
  263. crp->Colp = NULL;
  264. crp->Ncol = ++qrp->Nbcol;
  265. crp->Type = buftyp[i];
  266. crp->Length = length[i];
  267. crp->Clen = GetTypeSize(crp->Type, length[i]);
  268. crp->Prec = 0;
  269. if (ids > 0) {
  270. #if defined(XMSG)
  271. // Get header from message file
  272. strncpy(cname, PlugReadMessage(g, ids + crp->Ncol, NULL), NAM_LEN);
  273. cname[NAM_LEN] = 0; // for truncated long names
  274. //#elif defined(WIN32)
  275. // Get header from ressource file
  276. // LoadString(s_hModule, ids + crp->Ncol, cname, sizeof(cname));
  277. #else // !WIN32
  278. GetRcString(ids + crp->Ncol, cname, sizeof(cname));
  279. #endif // !WIN32
  280. crp->Name = (PSZ)PlugSubAlloc(g, NULL, strlen(cname) + 1);
  281. strcpy(crp->Name, cname);
  282. } else
  283. crp->Name = NULL; // Will be set by caller
  284. if (fldtyp)
  285. crp->Fld = fldtyp[i];
  286. else
  287. crp->Fld = FLD_NO;
  288. // Allocate the Value Block that will contain data
  289. if (crp->Length || nonull)
  290. crp->Kdata = AllocValBlock(g, NULL, crp->Type, maxres,
  291. crp->Length, 0, true, blank, false);
  292. else
  293. crp->Kdata = NULL;
  294. if (g->Trace)
  295. htrc("Column(%d) %s type=%d len=%d value=%p\n",
  296. crp->Ncol, crp->Name, crp->Type, crp->Length, crp->Kdata);
  297. } // endfor i
  298. *pcrp = NULL;
  299. return qrp;
  300. } // end of PlgAllocResult
  301. /***********************************************************************/
  302. /* Allocate and initialize the new DB User Block. */
  303. /***********************************************************************/
  304. PDBUSER PlgMakeUser(PGLOBAL g)
  305. {
  306. PDBUSER dbuserp;
  307. if (!(dbuserp = (PDBUSER)PlugAllocMem(g, (uint)sizeof(DBUSERBLK)))) {
  308. sprintf(g->Message, MSG(MALLOC_ERROR), "PlgMakeUser");
  309. return NULL;
  310. } // endif dbuserp
  311. memset(dbuserp, 0, sizeof(DBUSERBLK));
  312. //dbuserp->Act2 = g->Activityp;
  313. //#if defined(UNIX)
  314. // dbuserp->LineLen = 160;
  315. //#else
  316. // dbuserp->LineLen = 78;
  317. //#endif
  318. //dbuserp->Maxres = MAXRES;
  319. //dbuserp->Maxlin = MAXLIN;
  320. //dbuserp->Maxbmp = MAXBMP;
  321. //dbuserp->AlgChoice = AMOD_AUTO;
  322. dbuserp->UseTemp = TMP_AUTO;
  323. dbuserp->Check = CHK_ALL;
  324. strcpy(dbuserp->Server, "CONNECT");
  325. return dbuserp;
  326. } // end of PlgMakeUser
  327. /***********************************************************************/
  328. /* PlgGetUser: returns DBUSER block pointer. */
  329. /***********************************************************************/
  330. PDBUSER PlgGetUser(PGLOBAL g)
  331. {
  332. PDBUSER dup = (PDBUSER)((g->Activityp) ? g->Activityp->Aptr : NULL);
  333. if (!dup)
  334. strcpy(g->Message, MSG(APPL_NOT_INIT));
  335. return dup;
  336. } // end of PlgGetUser
  337. /***********************************************************************/
  338. /* PlgGetCatalog: returns CATALOG class pointer. */
  339. /***********************************************************************/
  340. PCATLG PlgGetCatalog(PGLOBAL g, bool jump)
  341. {
  342. PDBUSER dbuserp = PlgGetUser(g);
  343. PCATLG cat = (dbuserp) ? dbuserp->Catalog : NULL;
  344. if (!cat && jump) {
  345. // Raise exception so caller doesn't have to check return value
  346. strcpy(g->Message, MSG(NO_ACTIVE_DB));
  347. longjmp(g->jumper[g->jump_level], 1);
  348. } // endif cat
  349. return cat;
  350. } // end of PlgGetCatalog
  351. /***********************************************************************/
  352. /* PlgGetCatalog: returns CATALOG class pointer. */
  353. /***********************************************************************/
  354. char *PlgGetDataPath(PGLOBAL g)
  355. {
  356. PCATLG cat = PlgGetCatalog(g, false);
  357. //if (!cat)
  358. // return GetIniString(g, NULL, "DataBase", "DataPath", "", plgini);
  359. return (cat) ? cat->GetDataPath() : NULL;
  360. } // end of PlgGetDataPath
  361. #if 0
  362. /***********************************************************************/
  363. /* PlgGetXdbPath: sets the fully qualified file name of a database */
  364. /* description file in lgn and the new datapath in dp. */
  365. /* New database description file is a Configuration Settings file */
  366. /* that will be used and updated in case of DB modifications such */
  367. /* as Insert into a VCT file. Look for it and use it if found. */
  368. /* By default the configuration file is DataPath\name.xdb but the */
  369. /* configuration file name may also be specified in Plugdb.ini. */
  370. /***********************************************************************/
  371. bool PlgSetXdbPath(PGLOBAL g, PSZ dbname, PSZ dbpath,
  372. char *lgn, int lgsize,
  373. char *path, int psize)
  374. {
  375. char *dp, datapath[_MAX_PATH], ft[_MAX_EXT] = ".xdb";
  376. int n;
  377. if (path) {
  378. dp = path;
  379. n = psize;
  380. } else {
  381. dp = datapath;
  382. n = sizeof(datapath);
  383. } // endif path
  384. GetPrivateProfileString("DataBase", "DataPath", "", dp, n, plgini);
  385. if (trace)
  386. htrc("PlgSetXdbPath: path=%s\n", dp);
  387. if (dbpath) {
  388. char fn[_MAX_FNAME];
  389. strcpy(lgn, dbpath);
  390. _splitpath(lgn, NULL, NULL, fn, NULL);
  391. if (!*fn) // Old style use command
  392. strcat(lgn, dbname);
  393. _splitpath(lgn, NULL, NULL, dbname, NULL); // Extract DB name
  394. } else if (strcspn(dbname, ":/\\.") < strlen(dbname)) {
  395. // dbname name contains the path name of the XDB file
  396. strcpy(lgn, dbname);
  397. _splitpath(lgn, NULL, NULL, dbname, NULL); // Extract DB name
  398. } else
  399. /*******************************************************************/
  400. /* New database description file is a Configuration Settings file */
  401. /* that will be used and updated in case of DB modifications such */
  402. /* as Insert into a VCT file. Look for it and use it if found. */
  403. /* By default the configuration file is DataPath\name.xdb but the */
  404. /* configuration file name may also be specified in Plugdb.ini. */
  405. /*******************************************************************/
  406. GetPrivateProfileString("DBnames", dbname, "", lgn, lgsize, plgini);
  407. if (*lgn) {
  408. #if !defined(UNIX)
  409. char drive[_MAX_DRIVE];
  410. char direc[_MAX_DIR];
  411. #endif
  412. char fname[_MAX_FNAME];
  413. char ftype[_MAX_EXT];
  414. _splitpath(lgn, NULL, NULL, fname, ftype);
  415. if (!*ftype)
  416. strcat(lgn, ft);
  417. else if (!stricmp(ftype, ".var")) {
  418. strcpy(g->Message, MSG(NO_MORE_VAR));
  419. return true;
  420. } // endif ftype
  421. // Given DB description path may be relative to data path
  422. PlugSetPath(lgn, lgn, dp);
  423. // New data path is the path of the configuration setting file
  424. #if !defined(UNIX)
  425. _splitpath(lgn, drive, direc, NULL, NULL);
  426. _makepath(dp, drive, direc, "", "");
  427. #else
  428. //#error This must be tested for trailing slash
  429. _splitpath(lgn, NULL, dp, NULL, NULL);
  430. #endif
  431. } else {
  432. // Try dbname[.ext] in the current directory
  433. strcpy(lgn, dbname);
  434. if (!strchr(dbname, '.'))
  435. strcat(lgn, ft);
  436. PlugSetPath(lgn, lgn, dp);
  437. } // endif lgn
  438. if (trace)
  439. htrc("PlgSetXdbPath: new DB description file=%s\n", lgn);
  440. return false;
  441. } // end of PlgSetXdbPath
  442. #endif // 0
  443. /***********************************************************************/
  444. /* Extract from a path name the required component. */
  445. /* This function assumes there is enough space in the buffer. */
  446. /***********************************************************************/
  447. #if 0
  448. char *ExtractFromPath(PGLOBAL g, char *pBuff, char *FileName, OPVAL op)
  449. {
  450. char *drive = NULL, *direc = NULL, *fname = NULL, *ftype = NULL;
  451. switch (op) { // Determine which part to extract
  452. #if !defined(UNIX)
  453. case OP_FDISK: drive = pBuff; break;
  454. #endif // !UNIX
  455. case OP_FPATH: direc = pBuff; break;
  456. case OP_FNAME: fname = pBuff; break;
  457. case OP_FTYPE: ftype = pBuff; break;
  458. default:
  459. sprintf(g->Message, MSG(INVALID_OPER), op, "ExtractFromPath");
  460. return NULL;
  461. } // endswitch op
  462. // Now do the extraction
  463. _splitpath(FileName, drive, direc, fname, ftype);
  464. return pBuff;
  465. } // end of PlgExtractFromPath
  466. #endif
  467. /***********************************************************************/
  468. /* Check the occurence and matching of a pattern against a string. */
  469. /* Because this function is only used for catalog name checking, */
  470. /* it must be case insensitive. */
  471. /***********************************************************************/
  472. static bool PlugCheckPattern(PGLOBAL g, LPCSTR string, LPCSTR pat)
  473. {
  474. if (pat && strlen(pat)) {
  475. // This leaves 512 bytes (MAX_STR / 2) for each components
  476. LPSTR name = g->Message + MAX_STR / 2;
  477. strlwr(strcpy(name, string));
  478. strlwr(strcpy(g->Message, pat)); // Can be modified by Eval
  479. return EvalLikePattern(name, g->Message);
  480. } else
  481. return true;
  482. } // end of PlugCheckPattern
  483. /***********************************************************************/
  484. /* PlugEvalLike: evaluates a LIKE clause. */
  485. /* Syntaxe: M like P escape C. strg->M, pat->P, C not implemented yet */
  486. /***********************************************************************/
  487. bool PlugEvalLike(PGLOBAL g, LPCSTR strg, LPCSTR pat, bool ci)
  488. {
  489. char *tp, *sp;
  490. bool b;
  491. if (trace)
  492. htrc("LIKE: strg='%s' pattern='%s'\n", strg, pat);
  493. if (ci) { /* Case insensitive test */
  494. if (strlen(pat) + strlen(strg) + 1 < MAX_STR)
  495. tp = g->Message;
  496. else if (!(tp = new char[strlen(pat) + strlen(strg) + 2])) {
  497. strcpy(g->Message, MSG(NEW_RETURN_NULL));
  498. longjmp(g->jumper[g->jump_level], OP_LIKE);
  499. } /* endif tp */
  500. sp = tp + strlen(pat) + 1;
  501. strlwr(strcpy(tp, pat)); /* Make a lower case copy of pat */
  502. strlwr(strcpy(sp, strg)); /* Make a lower case copy of strg */
  503. } else { /* Case sensitive test */
  504. if (strlen(pat) < MAX_STR) /* In most of the case for small pat */
  505. tp = g->Message; /* Use this as temporary work space. */
  506. else if (!(tp = new char[strlen(pat) + 1])) {
  507. strcpy(g->Message, MSG(NEW_RETURN_NULL));
  508. longjmp(g->jumper[g->jump_level], OP_LIKE);
  509. } /* endif tp */
  510. strcpy(tp, pat); /* Make a copy to be worked into */
  511. sp = (char*)strg;
  512. } /* endif ci */
  513. b = EvalLikePattern(sp, tp);
  514. if (tp != g->Message) /* If working space was obtained */
  515. delete [] tp; /* by the use of new, delete it. */
  516. return (b);
  517. } /* end of PlugEvalLike */
  518. /***********************************************************************/
  519. /* M and P are variable length character string. If M and P are zero */
  520. /* length strings then the Like predicate is true. */
  521. /* */
  522. /* The Like predicate is true if: */
  523. /* */
  524. /* 1- A subtring of M is a sequence of 0 or more contiguous <CR> of M */
  525. /* and each <CR> of M is part of exactly one substring. */
  526. /* */
  527. /* 2- If the i-th <subtring-specifyer> of P is an <arbitrary-char- */
  528. /* specifier>, the i-th subtring of M is any single <CR>. */
  529. /* */
  530. /* 3- If the i-th <subtring-specifyer> of P is an <arbitrary-string- */
  531. /* specifier>, then the i-th subtring of M is any sequence of zero */
  532. /* or more <CR>. */
  533. /* */
  534. /* 4- If the i-th <subtring-specifyer> of P is neither an <arbitrary- */
  535. /* character-specifier> nor an <arbitrary-string-specifier>, then */
  536. /* the i-th substring of M is equal to that <substring-specifier> */
  537. /* according to the collating sequence of the <like-predicate>, */
  538. /* without the appending of <space-character>, and has the same */
  539. /* length as that <substring-specifier>. */
  540. /* */
  541. /* 5- The number of substrings of M is equal to the number of */
  542. /* <subtring-specifiers> of P. */
  543. /* */
  544. /* Otherwise M like P is false. */
  545. /***********************************************************************/
  546. bool EvalLikePattern(LPCSTR sp, LPCSTR tp)
  547. {
  548. LPSTR p;
  549. char c;
  550. int n;
  551. bool b, t = false;
  552. if (trace)
  553. htrc("Eval Like: sp=%s tp=%s\n",
  554. (sp) ? sp : "Null", (tp) ? tp : "Null");
  555. /********************************************************************/
  556. /* If pattern is void, Like is true only if string is also void. */
  557. /********************************************************************/
  558. if (!*tp)
  559. return (!*sp);
  560. /********************************************************************/
  561. /* Analyse eventual arbitrary specifications ahead of pattern. */
  562. /********************************************************************/
  563. for (p = (LPSTR)tp; p;)
  564. switch (*p) { /* it can contain % and/or _ */
  565. case '%': /* An % has been found */
  566. t = true; /* Note eventual character skip */
  567. p++;
  568. break;
  569. case '_': /* An _ has been found */
  570. if (*sp) { /* If more character in string */
  571. sp++; /* skip it */
  572. p++;
  573. } else
  574. return false; /* Like condition is not met */
  575. break;
  576. default:
  577. tp = p; /* Point to rest of template */
  578. p = NULL; /* To stop For loop */
  579. break;
  580. } /* endswitch */
  581. if ((p = (LPSTR)strpbrk(tp, "%_"))) /* Get position of next % or _ */
  582. n = p - tp;
  583. else
  584. n = strlen(tp); /* Get length of pattern head */
  585. if (trace)
  586. htrc(" testing: t=%d sp=%s tp=%s p=%p\n", t, sp, tp, p);
  587. if (n > (signed)strlen(sp)) /* If head is longer than strg */
  588. b = false; /* Like condition is not met */
  589. else if (n == 0) /* If void <substring-specifier> */
  590. b = (t || !*sp); /* true if % or void strg. */
  591. else if (!t) {
  592. /*******************************************************************/
  593. /* No character to skip, check occurence of <subtring-specifier> */
  594. /* at the very beginning of remaining string. */
  595. /*******************************************************************/
  596. if (p) {
  597. if ((b = !strncmp(sp, tp, n)))
  598. b = EvalLikePattern(sp + n, p);
  599. } else
  600. b = !strcmp(sp, tp); /* strg and tmp heads match */
  601. } else
  602. if (p)
  603. /*****************************************************************/
  604. /* Here is the case explaining why we need a recursive routine. */
  605. /* The test must be done not only against the first occurence */
  606. /* of the <substring-specifier> in the remaining string, */
  607. /* but also with all eventual succeeding ones. */
  608. /*****************************************************************/
  609. for (b = false, c = *p; !b && (signed)strlen(sp) >= n; sp++) {
  610. *p = '\0'; /* Separate pattern header */
  611. if ((sp = strstr(sp, tp))) {
  612. *p = c;
  613. b = EvalLikePattern(sp + n, p);
  614. } else {
  615. *p = c;
  616. b = false;
  617. break;
  618. } /* endif s */
  619. } /* endfor b, sp */
  620. else {
  621. sp += (strlen(sp) - n);
  622. b = !strcmp(sp, tp);
  623. } /* endif p */
  624. if (trace)
  625. htrc(" done: b=%d n=%d sp=%s tp=%s\n",
  626. b, n, (sp) ? sp : "Null", tp);
  627. return (b);
  628. } /* end of EvalLikePattern */
  629. /***********************************************************************/
  630. /* MakeEscape: Escape some characters in a string. */
  631. /***********************************************************************/
  632. char *MakeEscape(PGLOBAL g, char* str, char q)
  633. {
  634. char *bufp;
  635. int i, k, n = 0, len = (int)strlen(str);
  636. for (i = 0; i < len; i++)
  637. if (str[i] == q || str[i] == '\\')
  638. n++;
  639. if (!n)
  640. return str;
  641. else
  642. bufp = (char*)PlugSubAlloc(g, NULL, len + n + 1);
  643. for (i = k = 0; i < len; i++) {
  644. if (str[i] == q || str[i] == '\\')
  645. bufp[k++] = '\\';
  646. bufp[k++] = str[i];
  647. } // endfor i
  648. bufp[k] = 0;
  649. return bufp;
  650. } /* end of MakeEscape */
  651. /***********************************************************************/
  652. /* PlugConvertConstant: convert a Plug constant to an Xobject. */
  653. /***********************************************************************/
  654. void PlugConvertConstant(PGLOBAL g, void* & value, short& type)
  655. {
  656. if (trace)
  657. htrc("PlugConvertConstant: value=%p type=%hd\n", value, type);
  658. if (type != TYPE_XOBJECT) {
  659. value = new(g) CONSTANT(g, value, type);
  660. type = TYPE_XOBJECT;
  661. } // endif type
  662. } // end of PlugConvertConstant
  663. /***********************************************************************/
  664. /* Call the Flex preparser to convert a date format to a sscanf input */
  665. /* format and a Strftime output format. Flag if not 0 indicates that */
  666. /* non quoted blanks are not included in the output format. */
  667. /***********************************************************************/
  668. PDTP MakeDateFormat(PGLOBAL g, PSZ dfmt, bool in, bool out, int flag)
  669. {
  670. PDTP pdp = (PDTP)PlugSubAlloc(g, NULL, sizeof(DATPAR));
  671. if (trace)
  672. htrc("MakeDateFormat: dfmt=%s\n", dfmt);
  673. memset(pdp, 0, sizeof(DATPAR));
  674. pdp->Format = pdp->Curp = dfmt;
  675. pdp->Outsize = 2 * strlen(dfmt) + 1;
  676. if (in)
  677. pdp->InFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize);
  678. if (out)
  679. pdp->OutFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize);
  680. pdp->Flag = flag;
  681. /*********************************************************************/
  682. /* Call the FLEX generated parser. In multi-threading mode the next */
  683. /* instruction is included in an Enter/LeaveCriticalSection bracket. */
  684. /*********************************************************************/
  685. #if defined(THREAD)
  686. #if defined(WIN32)
  687. EnterCriticalSection((LPCRITICAL_SECTION)&parsec);
  688. #else // !WIN32
  689. pthread_mutex_lock(&parmut);
  690. #endif // !WIN32
  691. #endif // THREAD
  692. /*int rc =*/ fmdflex(pdp);
  693. #if defined(THREAD)
  694. #if defined(WIN32)
  695. LeaveCriticalSection((LPCRITICAL_SECTION)&parsec);
  696. #else // !WIN32
  697. pthread_mutex_unlock(&parmut);
  698. #endif // !WIN32
  699. #endif // THREAD
  700. if (trace)
  701. htrc("Done: in=%s out=%s\n", SVP(pdp->InFmt), SVP(pdp->OutFmt));
  702. return pdp;
  703. } // end of MakeDateFormat
  704. /***********************************************************************/
  705. /* Extract the date from a formatted string according to format. */
  706. /***********************************************************************/
  707. int ExtractDate(char *dts, PDTP pdp, int defy, int val[6])
  708. {
  709. char *fmt, c, d, e, W[8][12];
  710. int i, k, m, numval;
  711. int n, y = 30;
  712. if (pdp)
  713. fmt = pdp->InFmt;
  714. else // assume standard MySQL date format
  715. fmt = "%4d-%2d-%2d %2d:%2d:%2d";
  716. if (trace)
  717. htrc("ExtractDate: dts=%s fmt=%s defy=%d\n", dts, fmt, defy);
  718. // Set default values for time only use
  719. if (defy) {
  720. // This may be a default value for year
  721. y = defy;
  722. val[0] = y;
  723. y = (y < 100) ? y : 30;
  724. } else
  725. val[0] = 70;
  726. val[1] = 1;
  727. val[2] = 1;
  728. for (i = 3; i < 6; i++)
  729. val[i] = 0;
  730. numval = 0;
  731. // Get the date field parse it with derived input format
  732. m = sscanf(dts, fmt, W[0], W[1], W[2], W[3], W[4], W[5], W[6], W[7]);
  733. if (m > pdp->Num)
  734. m = pdp->Num;
  735. for (i = 0; i < m; i++) {
  736. n = *(int*)W[i];
  737. switch (k = pdp->Index[i]) {
  738. case 0:
  739. if (n < y)
  740. n += 100;
  741. val[0] = n;
  742. numval = max(numval, 1);
  743. break;
  744. case 1:
  745. case 2:
  746. case 3:
  747. case 4:
  748. case 5:
  749. val[k] = n;
  750. numval = max(numval, k + 1);
  751. break;
  752. case -1:
  753. c = toupper(W[i][0]);
  754. d = toupper(W[i][1]);
  755. e = toupper(W[i][2]);
  756. switch (c) {
  757. case 'J':
  758. n = (d == 'A') ? 1
  759. : (e == 'N') ? 6 : 7; break;
  760. case 'F': n = 2; break;
  761. case 'M':
  762. n = (e == 'R') ? 3 : 5; break;
  763. case 'A':
  764. n = (d == 'P') ? 4 : 8; break;
  765. break;
  766. case 'S': n = 9; break;
  767. case 'O': n = 10; break;
  768. case 'N': n = 11; break;
  769. case 'D': n = 12; break;
  770. } /* endswitch c */
  771. val[1] = n;
  772. numval = max(numval, 2);
  773. break;
  774. case -6:
  775. c = toupper(W[i][0]);
  776. n = val[3] % 12;
  777. if (c == 'P')
  778. n += 12;
  779. val[3] = n;
  780. break;
  781. } // endswitch Plugpar
  782. } // endfor i
  783. if (trace)
  784. htrc("numval=%d val=(%d,%d,%d,%d,%d,%d)\n",
  785. numval, val[0], val[1], val[2], val[3], val[4], val[5]);
  786. return numval;
  787. } // end of ExtractDate
  788. /***********************************************************************/
  789. /* Open file routine: the purpose of this routine is to make a list */
  790. /* of all open file so they can be closed in SQLINIT on error jump. */
  791. /***********************************************************************/
  792. FILE *PlugOpenFile(PGLOBAL g, LPCSTR fname, LPCSTR ftype)
  793. {
  794. FILE *fop;
  795. PFBLOCK fp;
  796. PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
  797. if (trace) {
  798. htrc("PlugOpenFile: fname=%s ftype=%s\n", fname, ftype);
  799. htrc("dbuserp=%p\n", dbuserp);
  800. } // endif trace
  801. if ((fop= global_fopen(g, MSGID_OPEN_MODE_STRERROR, fname, ftype)) != NULL) {
  802. if (trace)
  803. htrc(" fop=%p\n", fop);
  804. fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
  805. if (trace)
  806. htrc(" fp=%p\n", fp);
  807. // fname may be in volatile memory such as stack
  808. fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(fname) + 1);
  809. strcpy((char*)fp->Fname, fname);
  810. fp->Count = 1;
  811. fp->Type = TYPE_FB_FILE;
  812. fp->File = fop;
  813. fp->Mode = MODE_ANY; // ???
  814. fp->Next = dbuserp->Openlist;
  815. dbuserp->Openlist = fp;
  816. } /* endif fop */
  817. if (trace)
  818. htrc(" returning fop=%p\n", fop);
  819. return (fop);
  820. } // end of PlugOpenFile
  821. /***********************************************************************/
  822. /* Close file routine: the purpose of this routine is to avoid */
  823. /* double closing that freeze the system on some Unix platforms. */
  824. /***********************************************************************/
  825. int PlugCloseFile(PGLOBAL g, PFBLOCK fp, bool all)
  826. {
  827. int rc = 0;
  828. if (trace)
  829. htrc("PlugCloseFile: fp=%p count=%hd type=%hd\n",
  830. fp, ((fp) ? fp->Count : 0), ((fp) ? fp->Type : 0));
  831. if (!fp || !fp->Count)
  832. return rc;
  833. switch (fp->Type) {
  834. case TYPE_FB_FILE:
  835. if (fclose((FILE *)fp->File) == EOF)
  836. rc = errno;
  837. fp->File = NULL;
  838. fp->Mode = MODE_ANY;
  839. fp->Count = 0;
  840. break;
  841. case TYPE_FB_MAP:
  842. if ((fp->Count = (all) ? 0 : fp->Count - 1))
  843. break;
  844. if (CloseMemMap(fp->Memory, fp->Length))
  845. rc = (int)GetLastError();
  846. fp->Memory = NULL;
  847. fp->Mode = MODE_ANY;
  848. // Passthru
  849. case TYPE_FB_HANDLE:
  850. if (fp->Handle && fp->Handle != INVALID_HANDLE_VALUE)
  851. if (CloseFileHandle(fp->Handle))
  852. rc = (rc) ? rc : (int)GetLastError();
  853. fp->Handle = INVALID_HANDLE_VALUE;
  854. fp->Mode = MODE_ANY;
  855. fp->Count = 0;
  856. break;
  857. #ifdef DOMDOC_SUPPORT
  858. case TYPE_FB_XML:
  859. CloseXMLFile(g, fp, all);
  860. break;
  861. #endif // DOMDOC_SUPPORT
  862. #ifdef LIBXML2_SUPPORT
  863. case TYPE_FB_XML2:
  864. CloseXML2File(g, fp, all);
  865. break;
  866. #endif // LIBXML2_SUPPORT
  867. default:
  868. rc = RC_FX;
  869. } // endswitch Type
  870. return rc;
  871. } // end of PlugCloseFile
  872. /***********************************************************************/
  873. /* PlugCleanup: Cleanup remaining items of a SQL query. */
  874. /***********************************************************************/
  875. void PlugCleanup(PGLOBAL g, bool dofree)
  876. {
  877. PCATLG cat;
  878. PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
  879. // The test on Catalog is to avoid a Windows bug that can make
  880. // LoadString in PlugGetMessage to fail in some case
  881. if (!dbuserp || !(cat = dbuserp->Catalog))
  882. return;
  883. /*********************************************************************/
  884. /* Close eventually still open/mapped files. */
  885. /*********************************************************************/
  886. for (PFBLOCK fp = dbuserp->Openlist; fp; fp = fp->Next)
  887. PlugCloseFile(g, fp, true);
  888. dbuserp->Openlist = NULL;
  889. if (dofree) {
  890. /*******************************************************************/
  891. /* Cleanup any non suballocated memory still not freed. */
  892. /*******************************************************************/
  893. for (PMBLOCK mp = dbuserp->Memlist; mp; mp = mp->Next)
  894. PlgDBfree(*mp);
  895. dbuserp->Memlist = NULL;
  896. /*******************************************************************/
  897. /* If not using permanent storage catalog, reset volatile values. */
  898. /*******************************************************************/
  899. cat->Reset();
  900. /*******************************************************************/
  901. /* This is the place to reset the pointer on domains. */
  902. /*******************************************************************/
  903. dbuserp->Subcor = false;
  904. dbuserp->Step = STEP(PARSING_QUERY);
  905. dbuserp->ProgMax = dbuserp->ProgCur = dbuserp->ProgSav = 0;
  906. } // endif dofree
  907. } // end of PlugCleanup
  908. #if 0
  909. /***********************************************************************/
  910. /* That stupid Windows 98 does not provide this function. */
  911. /***********************************************************************/
  912. bool WritePrivateProfileInt(LPCSTR sec, LPCSTR key, int n, LPCSTR ini)
  913. {
  914. char buf[12];
  915. sprintf(buf, "%d", n);
  916. return WritePrivateProfileString(sec, key, buf, ini);
  917. } // end of WritePrivateProfileInt
  918. /***********************************************************************/
  919. /* Retrieve a size from an INI file with eventual K or M following. */
  920. /***********************************************************************/
  921. int GetIniSize(char *section, char *key, char *def, char *ini)
  922. {
  923. char c, buff[32];
  924. int i;
  925. int n = 0;
  926. GetPrivateProfileString(section, key, def, buff, sizeof(buff), ini);
  927. if ((i = sscanf(buff, " %d %c ", &n, &c)) == 2)
  928. switch (toupper(c)) {
  929. case 'M':
  930. n *= 1024;
  931. case 'K':
  932. n *= 1024;
  933. } // endswitch c
  934. if (trace)
  935. htrc("GetIniSize: key=%s buff=%s i=%d n=%d\n", key, buff, i, n);
  936. return n;
  937. } // end of GetIniSize
  938. /***********************************************************************/
  939. /* Allocate a string retrieved from an INI file and return its address */
  940. /***********************************************************************/
  941. DllExport PSZ GetIniString(PGLOBAL g, void *mp, LPCSTR sec, LPCSTR key,
  942. LPCSTR def, LPCSTR ini)
  943. {
  944. char buff[_MAX_PATH];
  945. PSZ p;
  946. int n, m = sizeof(buff);
  947. char *buf = buff;
  948. #if defined(_DEBUG)
  949. assert (sec && key);
  950. #endif
  951. again:
  952. n = GetPrivateProfileString(sec, key, def, buf, m, ini);
  953. if (n == m - 1) {
  954. // String may have been truncated, make sure to have all
  955. if (buf != buff)
  956. delete [] buf;
  957. m *= 2;
  958. buf = new char[m];
  959. goto again;
  960. } // endif n
  961. p = (PSZ)PlugSubAlloc(g, mp, n + 1);
  962. if (trace)
  963. htrc("GetIniString: sec=%s key=%s buf=%s\n", sec, key, buf);
  964. strcpy(p, buf);
  965. if (buf != buff)
  966. delete [] buf;
  967. return p;
  968. } // end of GetIniString
  969. #endif // 0
  970. /***********************************************************************/
  971. /* GetAmName: return the name correponding to an AM code. */
  972. /***********************************************************************/
  973. char *GetAmName(PGLOBAL g, AMT am, void *memp)
  974. {
  975. char *amn= (char*)PlugSubAlloc(g, memp, 16);
  976. switch (am) {
  977. case TYPE_AM_ERROR: strcpy(amn, "ERROR"); break;
  978. case TYPE_AM_ROWID: strcpy(amn, "ROWID"); break;
  979. case TYPE_AM_FILID: strcpy(amn, "FILID"); break;
  980. case TYPE_AM_VIEW: strcpy(amn, "VIEW"); break;
  981. case TYPE_AM_COUNT: strcpy(amn, "COUNT"); break;
  982. case TYPE_AM_DCD: strcpy(amn, "DCD"); break;
  983. case TYPE_AM_CMS: strcpy(amn, "CMS"); break;
  984. case TYPE_AM_MAP: strcpy(amn, "MAP"); break;
  985. case TYPE_AM_FMT: strcpy(amn, "FMT"); break;
  986. case TYPE_AM_CSV: strcpy(amn, "CSV"); break;
  987. case TYPE_AM_MCV: strcpy(amn, "MCV"); break;
  988. case TYPE_AM_DOS: strcpy(amn, "DOS"); break;
  989. case TYPE_AM_FIX: strcpy(amn, "FIX"); break;
  990. case TYPE_AM_BIN: strcpy(amn, "BIN"); break;
  991. case TYPE_AM_VCT: strcpy(amn, "VEC"); break;
  992. case TYPE_AM_VMP: strcpy(amn, "VMP"); break;
  993. case TYPE_AM_DBF: strcpy(amn, "DBF"); break;
  994. case TYPE_AM_QRY: strcpy(amn, "QRY"); break;
  995. case TYPE_AM_SQL: strcpy(amn, "SQL"); break;
  996. case TYPE_AM_PLG: strcpy(amn, "PLG"); break;
  997. case TYPE_AM_PLM: strcpy(amn, "PLM"); break;
  998. case TYPE_AM_DOM: strcpy(amn, "DOM"); break;
  999. case TYPE_AM_DIR: strcpy(amn, "DIR"); break;
  1000. case TYPE_AM_ODBC: strcpy(amn, "ODBC"); break;
  1001. case TYPE_AM_MAC: strcpy(amn, "MAC"); break;
  1002. case TYPE_AM_OEM: strcpy(amn, "OEM"); break;
  1003. case TYPE_AM_OUT: strcpy(amn, "OUT"); break;
  1004. default: sprintf(amn, "OEM(%d)", am);
  1005. } // endswitch am
  1006. return amn;
  1007. } // end of GetAmName
  1008. #if defined(WIN32) && !defined(NOCATCH)
  1009. /***********************************************************************/
  1010. /* GetExceptionDesc: return the description of an exception code. */
  1011. /***********************************************************************/
  1012. char *GetExceptionDesc(PGLOBAL g, unsigned int e)
  1013. {
  1014. char *p;
  1015. switch (e) {
  1016. case EXCEPTION_GUARD_PAGE:
  1017. p = MSG(GUARD_PAGE);
  1018. break;
  1019. case EXCEPTION_DATATYPE_MISALIGNMENT:
  1020. p = MSG(DATA_MISALIGN);
  1021. break;
  1022. case EXCEPTION_BREAKPOINT:
  1023. p = MSG(BREAKPOINT);
  1024. break;
  1025. case EXCEPTION_SINGLE_STEP:
  1026. p = MSG(SINGLE_STEP);
  1027. break;
  1028. case EXCEPTION_ACCESS_VIOLATION:
  1029. p = MSG(ACCESS_VIOLATN);
  1030. break;
  1031. case EXCEPTION_IN_PAGE_ERROR:
  1032. p = MSG(PAGE_ERROR);
  1033. break;
  1034. case EXCEPTION_INVALID_HANDLE:
  1035. p = MSG(INVALID_HANDLE);
  1036. break;
  1037. case EXCEPTION_ILLEGAL_INSTRUCTION:
  1038. p = MSG(ILLEGAL_INSTR);
  1039. break;
  1040. case EXCEPTION_NONCONTINUABLE_EXCEPTION:
  1041. p = MSG(NONCONT_EXCEPT);
  1042. break;
  1043. case EXCEPTION_INVALID_DISPOSITION:
  1044. p = MSG(INVALID_DISP);
  1045. break;
  1046. case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
  1047. p = MSG(ARRAY_BNDS_EXCD);
  1048. break;
  1049. case EXCEPTION_FLT_DENORMAL_OPERAND:
  1050. p = MSG(FLT_DENORMAL_OP);
  1051. break;
  1052. case EXCEPTION_FLT_DIVIDE_BY_ZERO:
  1053. p = MSG(FLT_ZERO_DIVIDE);
  1054. break;
  1055. case EXCEPTION_FLT_INEXACT_RESULT:
  1056. p = MSG(FLT_BAD_RESULT);
  1057. break;
  1058. case EXCEPTION_FLT_INVALID_OPERATION:
  1059. p = MSG(FLT_INVALID_OP);
  1060. break;
  1061. case EXCEPTION_FLT_OVERFLOW:
  1062. p = MSG(FLT_OVERFLOW);
  1063. break;
  1064. case EXCEPTION_FLT_STACK_CHECK:
  1065. p = MSG(FLT_STACK_CHECK);
  1066. break;
  1067. case EXCEPTION_FLT_UNDERFLOW:
  1068. p = MSG(FLT_UNDERFLOW);
  1069. break;
  1070. case EXCEPTION_INT_DIVIDE_BY_ZERO:
  1071. p = MSG(INT_ZERO_DIVIDE);
  1072. break;
  1073. case EXCEPTION_INT_OVERFLOW:
  1074. p = MSG(INT_OVERFLOW);
  1075. break;
  1076. case EXCEPTION_PRIV_INSTRUCTION:
  1077. p = MSG(PRIV_INSTR);
  1078. break;
  1079. case EXCEPTION_STACK_OVERFLOW:
  1080. p = MSG(STACK_OVERFLOW);
  1081. break;
  1082. case CONTROL_C_EXIT:
  1083. p = MSG(CONTROL_C_EXIT);
  1084. break;
  1085. case STATUS_NO_MEMORY:
  1086. p = MSG(NO_MEMORY);
  1087. break;
  1088. default:
  1089. p = MSG(UNKNOWN_EXCPT);
  1090. break;
  1091. } // endswitch nSE
  1092. return p;
  1093. } // end of GetExceptionDesc
  1094. #endif // WIN32 && !NOCATCH
  1095. /***********************************************************************/
  1096. /* PlgDBalloc: allocates or suballocates memory conditionally. */
  1097. /* If mp.Sub is true at entry, this forces suballocation. */
  1098. /* If the memory is allocated, makes an entry in an allocation list */
  1099. /* so it can be freed at the normal or error query completion. */
  1100. /***********************************************************************/
  1101. void *PlgDBalloc(PGLOBAL g, void *area, MBLOCK& mp)
  1102. {
  1103. //bool b;
  1104. size_t maxsub, minsub;
  1105. void *arp = (area) ? area : g->Sarea;
  1106. PPOOLHEADER pph = (PPOOLHEADER)arp;
  1107. if (mp.Memp) {
  1108. // This is a reallocation. If this block is not suballocated, it
  1109. // was already placed in the chain of memory blocks and we must
  1110. // not do it again as it can trigger a loop when freeing them.
  1111. // Note: this works if blocks can be reallocated only once.
  1112. // Otherwise a new boolean must be added to the block that
  1113. // indicate that it is chained, or a test on the whole chain be
  1114. // done to check whether the block is already there.
  1115. // b = mp.Sub;
  1116. mp.Sub = false; // Restrict suballocation to one quarter
  1117. } // endif Memp
  1118. // Suballoc when possible if mp.Sub is initially true, but leaving
  1119. // a minimum amount of storage for future operations such as the
  1120. // optimize recalculation after insert; otherwise
  1121. // suballoc only if size is smaller than one quarter of free mem.
  1122. minsub = (pph->FreeBlk + pph->To_Free + 524248) >> 2;
  1123. maxsub = (pph->FreeBlk < minsub) ? 0 : pph->FreeBlk - minsub;
  1124. mp.Sub = mp.Size <= ((mp.Sub) ? maxsub : (maxsub >> 2));
  1125. if (trace)
  1126. htrc("PlgDBalloc: in %p size=%d used=%d free=%d sub=%d\n",
  1127. arp, mp.Size, pph->To_Free, pph->FreeBlk, mp.Sub);
  1128. if (!mp.Sub) {
  1129. // For allocations greater than one fourth of remaining storage
  1130. // in the area, do allocate from virtual storage.
  1131. #if defined(WIN32)
  1132. if (mp.Size >= BIGMEM)
  1133. mp.Memp = VirtualAlloc(NULL, mp.Size, MEM_COMMIT, PAGE_READWRITE);
  1134. else
  1135. #endif
  1136. mp.Memp = malloc(mp.Size);
  1137. if (!mp.Inlist && mp.Memp) {
  1138. // New allocated block, put it in the memory block chain.
  1139. PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
  1140. mp.Next = dbuserp->Memlist;
  1141. dbuserp->Memlist = &mp;
  1142. mp.Inlist = true;
  1143. } // endif mp
  1144. } else
  1145. // Suballocating is Ok.
  1146. mp.Memp = PlugSubAlloc(g, area, mp.Size);
  1147. return mp.Memp;
  1148. } // end of PlgDBalloc
  1149. /***********************************************************************/
  1150. /* PlgDBrealloc: reallocates memory conditionally. */
  1151. /* Note that this routine can fail only when block size is increased */
  1152. /* because otherwise we keep the old storage on failure. */
  1153. /***********************************************************************/
  1154. void *PlgDBrealloc(PGLOBAL g, void *area, MBLOCK& mp, size_t newsize)
  1155. {
  1156. MBLOCK m;
  1157. #if defined(_DEBUG)
  1158. // assert (mp.Memp != NULL);
  1159. #endif
  1160. if (trace)
  1161. htrc("PlgDBrealloc: %p size=%d sub=%d\n", mp.Memp, mp.Size, mp.Sub);
  1162. if (newsize == mp.Size)
  1163. return mp.Memp; // Nothing to do
  1164. else
  1165. m = mp;
  1166. if (!mp.Sub && mp.Size < BIGMEM && newsize < BIGMEM) {
  1167. // Allocation was done by malloc, try to use realloc but
  1168. // suballoc if newsize is smaller than one quarter of free mem.
  1169. size_t maxsub;
  1170. PPOOLHEADER pph = (PPOOLHEADER)((area) ? area : g->Sarea);
  1171. maxsub = (pph->FreeBlk < 131072) ? 0 : pph->FreeBlk - 131072;
  1172. if ((mp.Sub = (newsize <= (maxsub >> 2)))) {
  1173. mp.Memp = PlugSubAlloc(g, area, newsize);
  1174. memcpy(mp.Memp, m.Memp, min(m.Size, newsize));
  1175. PlgDBfree(m); // Free the old block
  1176. } else if (!(mp.Memp = realloc(mp.Memp, newsize))) {
  1177. mp = m; // Possible only if newsize > Size
  1178. return NULL; // Failed
  1179. } // endif's
  1180. mp.Size = newsize;
  1181. } else if (!mp.Sub || newsize > mp.Size) {
  1182. // Was suballocated or Allocation was done by VirtualAlloc
  1183. // Make a new allocation and copy the useful part
  1184. // Note: DO NOT reset Memp and Sub so we know that this
  1185. // is a reallocation in PlgDBalloc
  1186. mp.Size = newsize;
  1187. if (PlgDBalloc(g, area, mp)) {
  1188. memcpy(mp.Memp, m.Memp, min(m.Size, newsize));
  1189. PlgDBfree(m); // Free the old block
  1190. } else {
  1191. mp = m; // No space to realloc, do nothing
  1192. if (newsize > m.Size)
  1193. return NULL; // Failed
  1194. } // endif PlgDBalloc
  1195. } // endif's
  1196. if (trace)
  1197. htrc(" newsize=%d newp=%p sub=%d\n", mp.Size, mp.Memp, mp.Sub);
  1198. return mp.Memp;
  1199. } // end of PlgDBrealloc
  1200. /***********************************************************************/
  1201. /* PlgDBfree: free memory if not suballocated. */
  1202. /***********************************************************************/
  1203. void PlgDBfree(MBLOCK& mp)
  1204. {
  1205. if (trace)
  1206. htrc("PlgDBfree: %p sub=%d size=%d\n", mp.Memp, mp.Sub, mp.Size);
  1207. if (!mp.Sub && mp.Memp)
  1208. #if defined(WIN32)
  1209. if (mp.Size >= BIGMEM)
  1210. VirtualFree(mp.Memp, 0, MEM_RELEASE);
  1211. else
  1212. #endif
  1213. free(mp.Memp);
  1214. // Do not reset Next to avoid cutting the Mblock chain
  1215. mp.Memp = NULL;
  1216. mp.Sub = false;
  1217. mp.Size = 0;
  1218. } // end of PlgDBfree
  1219. #if 0 // Not used yet
  1220. /***********************************************************************/
  1221. /* Program for sub-allocating one item in a storage area. */
  1222. /* Note: This function is equivalent to PlugSubAlloc except that in */
  1223. /* case of insufficient memory, it returns NULL instead of doing a */
  1224. /* long jump. The caller must test the return value for error. */
  1225. /***********************************************************************/
  1226. void *PlgDBSubAlloc(PGLOBAL g, void *memp, size_t size)
  1227. {
  1228. PPOOLHEADER pph; // Points on area header.
  1229. if (!memp)
  1230. /*******************************************************************/
  1231. /* Allocation is to be done in the Sarea. */
  1232. /*******************************************************************/
  1233. memp = g->Sarea;
  1234. size = ((size + 3) / 4) * 4; /* Round up size to multiple of 4 */
  1235. //size = ((size + 7) / 8) * 8; /* Round up size to multiple of 8 */
  1236. pph = (PPOOLHEADER)memp;
  1237. #if defined(DEBTRACE)
  1238. htrc("PlgDBSubAlloc: memp=%p size=%d used=%d free=%d\n",
  1239. memp, size, pph->To_Free, pph->FreeBlk);
  1240. #endif
  1241. if ((uint)size > pph->FreeBlk) { /* Not enough memory left in pool */
  1242. char *pname = NULL;
  1243. PACTIVITY ap;
  1244. if (memp == g->Sarea)
  1245. pname = "Work";
  1246. else if ((ap = g->Activityp)) {
  1247. if (memp == ap->LangRulep)
  1248. pname = "Rule";
  1249. else if (memp == ap->Nodep[0])
  1250. pname = "Dictionary";
  1251. else if (memp == ap->Nodep[1])
  1252. pname = "Vartok";
  1253. else if (memp == ap->Nodep[2])
  1254. pname = "Lexicon";
  1255. else if (memp == ap->User_Dictp)
  1256. pname = "User dictionary";
  1257. else if (ap->Aptr)
  1258. pname = "Application";
  1259. } // endif memp
  1260. if (pname)
  1261. sprintf(g->Message,
  1262. "Not enough memory in %s area for request of %d (used=%d free=%d)",
  1263. pname, size, pph->To_Free, pph->FreeBlk);
  1264. else
  1265. sprintf(g->Message, MSG(SUBALLOC_ERROR),
  1266. memp, size, pph->To_Free, pph->FreeBlk);
  1267. #if defined(DEBTRACE)
  1268. htrc("%s\n", g->Message);
  1269. #endif
  1270. return NULL;
  1271. } // endif size
  1272. /*********************************************************************/
  1273. /* Do the suballocation the simplest way. */
  1274. /*********************************************************************/
  1275. memp = MakePtr(memp, pph->To_Free); // Points to suballocated block
  1276. pph->To_Free += size; // New offset of pool free block
  1277. pph->FreeBlk -= size; // New size of pool free block
  1278. #if defined(DEBTRACE)
  1279. htrc("Done memp=%p used=%d free=%d\n",
  1280. memp, pph->To_Free, pph->FreeBlk);
  1281. #endif
  1282. return (memp);
  1283. } // end of PlgDBSubAlloc
  1284. #endif // 0 Not used yet
  1285. /***********************************************************************/
  1286. /* PUTOUT: Plug DB object typing routine. */
  1287. /***********************************************************************/
  1288. void PlugPutOut(PGLOBAL g, FILE *f, short t, void *v, uint n)
  1289. {
  1290. char m[64];
  1291. if (trace)
  1292. htrc("PUTOUT: f=%p t=%d v=%p n=%d\n", f, t, v, n);
  1293. if (!v)
  1294. return;
  1295. memset(m, ' ', n); /* Make margin string */
  1296. m[n] = '\0';
  1297. n += 2; /* Increase margin */
  1298. switch (t) {
  1299. case TYPE_ERROR:
  1300. fprintf(f, "--> %s\n", (PSZ)v);
  1301. break;
  1302. case TYPE_STRING:
  1303. case TYPE_PSZ:
  1304. fprintf(f, "%s%s\n", m, (PSZ)v);
  1305. break;
  1306. case TYPE_FLOAT:
  1307. fprintf(f, "%s%lf\n", m, *(double *)v);
  1308. break;
  1309. case TYPE_LIST:
  1310. case TYPE_COLIST:
  1311. case TYPE_COL:
  1312. {PPARM p;
  1313. if (t == TYPE_LIST)
  1314. fprintf(f, "%s%s\n", m, MSG(LIST));
  1315. else
  1316. fprintf(f, "%s%s\n", m, "Colist:");
  1317. for (p = (PPARM)v; p; p = p->Next)
  1318. PlugPutOut(g, f, p->Type, p->Value, n);
  1319. } break;
  1320. case TYPE_INT:
  1321. fprintf(f, "%s%d\n", m, *(int *)v);
  1322. break;
  1323. case TYPE_SHORT:
  1324. fprintf(f, "%s%hd\n", m, *(short *)v);
  1325. break;
  1326. case TYPE_TINY:
  1327. fprintf(f, "%s%d\n", m, (int)*(char *)v);
  1328. break;
  1329. case TYPE_VOID:
  1330. break;
  1331. case TYPE_SQL:
  1332. case TYPE_TABLE:
  1333. case TYPE_TDB:
  1334. case TYPE_XOBJECT:
  1335. ((PBLOCK)v)->Print(g, f, n-2);
  1336. break;
  1337. default:
  1338. fprintf(f, "%s%s %d\n", m, MSG(ANSWER_TYPE), t);
  1339. } /* endswitch */
  1340. return;
  1341. } /* end of PlugPutOut */
  1342. /***********************************************************************/
  1343. /* NewPointer: makes a table of pointer values to be changed later. */
  1344. /***********************************************************************/
  1345. DllExport void NewPointer(PTABS t, void *oldv, void *newv)
  1346. {
  1347. PTABPTR tp;
  1348. if (!oldv) /* error ?????????? */
  1349. return;
  1350. if (!t->P1 || t->P1->Num == 50)
  1351. {
  1352. if (!(tp = new TABPTR)) {
  1353. PGLOBAL g = t->G;
  1354. sprintf(g->Message, "NewPointer: %s", MSG(MEM_ALLOC_ERROR));
  1355. longjmp(g->jumper[g->jump_level], 3);
  1356. } else {
  1357. tp->Next = t->P1;
  1358. tp->Num = 0;
  1359. t->P1 = tp;
  1360. } /* endif tp */
  1361. }
  1362. t->P1->Old[t->P1->Num] = oldv;
  1363. t->P1->New[t->P1->Num++] = newv;
  1364. } /* end of NewPointer */
  1365. #if 0
  1366. /***********************************************************************/
  1367. /* Compare two files and return 0 if they are identical, else 1. */
  1368. /***********************************************************************/
  1369. int FileComp(PGLOBAL g, char *file1, char *file2)
  1370. {
  1371. char *fn[2], *bp[2], buff1[4096], buff2[4096];
  1372. int i, k, n[2], h[2] = {-1,-1};
  1373. int len[2], rc = -1;
  1374. fn[0] = file1; fn[1] = file2;
  1375. bp[0] = buff1; bp[1] = buff2;
  1376. for (i = 0; i < 2; i++) {
  1377. #if defined(WIN32)
  1378. h[i]= global_open(g, MSGID_NONE, fn[i], _O_RDONLY | _O_BINARY);
  1379. #else // !WIN32
  1380. h[i]= global_open(g, MSGOD_NONE, fn[i], O_RDONLY);
  1381. #endif // !WIN32
  1382. if (h[i] == -1) {
  1383. // if (errno != ENOENT) {
  1384. sprintf(g->Message, MSG(OPEN_MODE_ERROR),
  1385. "rb", (int)errno, fn[i]);
  1386. strcat(strcat(g->Message, ": "), strerror(errno));
  1387. longjmp(g->jumper[g->jump_level], 666);
  1388. // } else
  1389. // len[i] = 0; // File does not exist yet
  1390. } else {
  1391. if ((len[i] = _filelength(h[i])) < 0) {
  1392. sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", fn[i]);
  1393. longjmp(g->jumper[g->jump_level], 666);
  1394. } // endif len
  1395. } // endif h
  1396. } // endfor i
  1397. if (len[0] != len[1])
  1398. rc = 1;
  1399. while (rc == -1) {
  1400. for (i = 0; i < 2; i++)
  1401. if ((n[i] = read(h[i], bp[i], 4096)) < 0) {
  1402. sprintf(g->Message, MSG(READ_ERROR), fn[i], strerror(errno));
  1403. goto fin;
  1404. } // endif n
  1405. if (n[0] != n[1])
  1406. rc = 1;
  1407. else if (*n == 0)
  1408. rc = 0;
  1409. else for (k = 0; k < *n; k++)
  1410. if (*(bp[0] + k) != *(bp[1] + k)) {
  1411. rc = 1;
  1412. goto fin;
  1413. } // endif bp
  1414. } // endwhile
  1415. fin:
  1416. for (i = 0; i < 2; i++)
  1417. if (h[i] != -1)
  1418. close(h[i]);
  1419. return rc;
  1420. } // end of FileComp
  1421. #endif // 0