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