Commit 2bb42803 authored by Olivier Bertrand's avatar Olivier Bertrand

This commit includes changes done in a previous (deleted) branch plus new ones.

From the previous branch:
commit eda4928ff122a0845baf5ade83b4aa29244a3a89
Author: Olivier Bertrand <bertrandop@gmail.com>
Date:   Mon Mar 9 22:34:56 2015 +0100

- Add discovery to JSON tables
  When columns are not defined, CONNECT analyses the json file to find column definitions.
  This wors only on table that are an array of objects. Pair keys are used to generate the
  column names and pair values are used for its definition. When the LEVEL option is defined
  as a not null integer, the eventual JPATH is scanned up to the LEVEL value.

From the current one:
- Fix MDEV-7521 when column names are utf8 encoded (not a general multi-charset fix)

- Adds more to JSON discovery processing and UDF's

- Use PlugDup everywhere it replaces PlugSubAlloc + strcpy.
parent 57aaccef
......@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
......@@ -207,8 +207,7 @@ bool MAPFAM::OpenTableFile(PGLOBAL g)
/*******************************************************************/
fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
fp->Type = TYPE_FB_MAP;
fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
strcpy((char*)fp->Fname, filename);
fp->Fname = PlugDup(g, filename);
fp->Next = dbuserp->Openlist;
dbuserp->Openlist = fp;
fp->Count = 1;
......
......@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
......@@ -289,8 +289,7 @@ bool TXTFAM::AddListValue(PGLOBAL g, int type, void *val, PPARM *top)
pp->Intval = *(int*)val;
break;
// case TYPE_STRING:
// pp->Value = PlugSubAlloc(g, NULL, strlen((char*)val) + 1);
// strcpy((char*)pp->Value, (char*)val);
// pp->Value = PlugDup(g, (char*)val);
// break;
case TYPE_PCHAR:
pp->Value = val;
......@@ -325,8 +324,7 @@ int TXTFAM::StoreValues(PGLOBAL g, bool upd)
if (Tdbp->PrepareWriting(g))
return RC_FX;
buf = (char*)PlugSubAlloc(g, NULL, strlen(Tdbp->GetLine()) + 1);
strcpy(buf, Tdbp->GetLine());
buf = PlugDup(g, Tdbp->GetLine());
rc = AddListValue(g, TYPE_PCHAR, buf, &To_Upd);
} // endif upd
......
......@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
......@@ -1451,8 +1451,7 @@ bool VCMFAM::OpenTableFile(PGLOBAL g)
/*******************************************************************/
fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
fp->Type = TYPE_FB_MAP;
fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
strcpy((char*)fp->Fname, filename);
fp->Fname = PlugDup(g, filename);
fp->Next = dbuserp->Openlist;
dbuserp->Openlist = fp;
fp->Count = 1;
......@@ -2844,8 +2843,7 @@ bool VMPFAM::MapColumnFile(PGLOBAL g, MODE mode, int i)
/*******************************************************************/
fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
fp->Type = TYPE_FB_MAP;
fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
strcpy((char*)fp->Fname, filename);
fp->Fname = PlugDup(g, filename);
fp->Next = dup->Openlist;
dup->Openlist = fp;
fp->Count = 1;
......
......@@ -171,7 +171,7 @@
#define JSONMAX 10 // JSON Default max grp size
extern "C" {
char version[]= "Version 1.03.0006 February 06, 2015";
char version[]= "Version 1.03.0006 March 16, 2015";
#if defined(WIN32)
char compver[]= "Version 1.03.0006 " __DATE__ " " __TIME__;
......@@ -211,6 +211,8 @@ extern "C" {
/***********************************************************************/
PQRYRES OEMColumns(PGLOBAL g, PTOS topt, char *tab, char *db, bool info);
PQRYRES VirColumns(PGLOBAL g, char *tab, char *db, bool info);
PQRYRES JSONColumns(PGLOBAL g, char *dp, const char *fn, char *objn,
int pretty, int lvl, int mxr, bool info);
void PushWarning(PGLOBAL g, THD *thd, int level);
bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, const char *host,
const char *db, char *tab, const char *src, int port);
......@@ -1020,8 +1022,7 @@ char *GetListOption(PGLOBAL g, const char *opname,
} // endif pv
if (!stricmp(opname, key)) {
opval= (char*)PlugSubAlloc(g, NULL, strlen(val) + 1);
strcpy(opval, val);
opval= PlugDup(g, val);
break;
} else if (!pn)
break;
......@@ -1506,8 +1507,7 @@ PIXDEF ha_connect::GetIndexInfo(TABLE_SHARE *s)
// Now get index information
pn= (char*)s->keynames.type_names[n];
name= (char*)PlugSubAlloc(g, NULL, strlen(pn) + 1);
strcpy(name, pn); // This is probably unuseful
name= PlugDup(g, pn);
unique= (kp.flags & 1) != 0;
pkp= NULL;
......@@ -1517,8 +1517,7 @@ PIXDEF ha_connect::GetIndexInfo(TABLE_SHARE *s)
// Get the the key parts info
for (int k= 0; (unsigned)k < kp.user_defined_key_parts; k++) {
pn= (char*)kp.key_part[k].field->field_name;
name= (char*)PlugSubAlloc(g, NULL, strlen(pn) + 1);
strcpy(name, pn); // This is probably unuseful
name= PlugDup(g, pn);
// Allocate the key part description block
kpp= new(g) KPARTDEF(name, k + 1);
......@@ -4738,9 +4737,9 @@ static char *encode(PGLOBAL g, const char *cnm)
@return
Return 0 if ok
*/
static bool add_field(String *sql, const char *field_name, int typ,
int len, int dec, char *key, uint tm, const char *rem,
char *dft, char *xtra, int flag, bool dbf, char v)
static bool add_field(String *sql, const char *field_name, int typ, int len,
int dec, char *key, uint tm, const char *rem, char *dft,
char *xtra, char *fmt, int flag, bool dbf, char v)
{
char var = (len > 255) ? 'V' : v;
bool q, error= false;
......@@ -4809,6 +4808,12 @@ static bool add_field(String *sql, const char *field_name, int typ,
error|= sql->append("'");
} // endif rem
if (fmt && *fmt) {
error|= sql->append(" FIELD_FORMAT='");
error|= sql->append_for_single_quote(fmt, strlen(fmt));
error|= sql->append("'");
} // endif flag
if (flag) {
error|= sql->append(" FLAG=");
error|= sql->append_ulonglong(flag);
......@@ -4952,12 +4957,12 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
const char *fncn= "?";
const char *user, *fn, *db, *host, *pwd, *sep, *tbl, *src;
const char *col, *ocl, *rnk, *pic, *fcl, *skc;
char *tab, *dsn, *shm, *dpath;
char *tab, *dsn, *shm, *dpath, *objn;
#if defined(WIN32)
char *nsp= NULL, *cls= NULL;
#endif // WIN32
int port= 0, hdr= 0, mxr __attribute__((unused))= 0, mxe= 0, rc= 0;
int cop __attribute__((unused)) = 0;
int cop __attribute__((unused))= 0, pty= 2, lrecl= 0, lvl= 0;
#if defined(ODBC_SUPPORT)
POPARM sop = NULL;
char *ucnc = NULL;
......@@ -4987,7 +4992,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
if (!g)
return HA_ERR_INTERNAL_ERROR;
user= host= pwd= tbl= src= col= ocl= pic= fcl= skc= rnk= dsn= NULL;
user= host= pwd= tbl= src= col= ocl= pic= fcl= skc= rnk= dsn= objn= NULL;
// Get the useful create options
ttp= GetTypeID(topt->type);
......@@ -5003,6 +5008,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
hdr= (int)topt->header;
tbl= topt->tablist;
col= topt->colist;
lrecl= (int)topt->lrecl;
if (topt->oplist) {
host= GetListOption(g, "host", topt->oplist, "localhost");
......@@ -5017,6 +5023,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
skc= GetListOption(g, "skipcol", topt->oplist, NULL);
rnk= GetListOption(g, "rankcol", topt->oplist, NULL);
pwd= GetListOption(g, "password", topt->oplist);
objn= GetListOption(g, "Object", topt->oplist, NULL);
#if defined(WIN32)
nsp= GetListOption(g, "namespace", topt->oplist);
cls= GetListOption(g, "class", topt->oplist);
......@@ -5034,6 +5041,8 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
#if defined(PROMPT_OK)
cop= atoi(GetListOption(g, "checkdsn", topt->oplist, "0"));
#endif // PROMPT_OK
pty= atoi(GetListOption(g,"Pretty", topt->oplist, "2"));
lvl= atoi(GetListOption(g,"Level", topt->oplist, "0"));
} else {
host= "localhost";
user= (ttp == TAB_ODBC ? NULL : "root");
......@@ -5077,8 +5086,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
goto err;
} // endif tbl
tab= (char*)PlugSubAlloc(g, NULL, strlen(tbl) + 1);
strcpy(tab, tbl);
tab= PlugDup(g, tbl);
if ((p= strchr(tab, ',')))
*p= 0;
......@@ -5204,6 +5212,13 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
else
strcpy(g->Message, "Missing OEM module or subtype");
break;
case TAB_JSON:
if (!fn)
sprintf(g->Message, "Missing %s file name", topt->type);
else
ok= true;
break;
case TAB_VIR:
ok= true;
......@@ -5226,7 +5241,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
} // endif src
if (ok) {
char *cnm, *rem, *dft, *xtra, *key;
char *cnm, *rem, *dft, *xtra, *key, *fmt;
int i, len, prec, dec, typ, flg;
// if (cat)
......@@ -5315,6 +5330,9 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
case TAB_VIR:
qrp= VirColumns(g, tab, (char*)db, fnc == FNC_COL);
break;
case TAB_JSON:
qrp= JSONColumns(g, (char*)db, fn, objn, pty, lrecl, lvl, fnc == FNC_COL);
break;
case TAB_OEM:
qrp= OEMColumns(g, topt, tab, (char*)db, fnc == FNC_COL);
break;
......@@ -5331,7 +5349,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
if (fnc != FNC_NO || src || ttp == TAB_PIVOT) {
// Catalog like table
for (crp= qrp->Colresp; !rc && crp; crp= crp->Next) {
cnm= encode(g, crp->Name);
cnm= (ttp == TAB_PIVOT) ? crp->Name : encode(g, crp->Name);
typ= crp->Type;
len= crp->Length;
dec= crp->Prec;
......@@ -5347,7 +5365,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
NOT_NULL_FLAG, "", flg, dbf, v);
#else // !NEW_WAY
if (add_field(&sql, cnm, typ, len, dec, NULL, NOT_NULL_FLAG,
NULL, NULL, NULL, flg, dbf, v))
NULL, NULL, NULL, NULL, flg, dbf, v))
rc= HA_ERR_OUT_OF_MEM;
#endif // !NEW_WAY
} // endfor crp
......@@ -5368,7 +5386,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
typ= len= prec= dec= 0;
tm= NOT_NULL_FLAG;
cnm= (char*)"noname";
dft= xtra= key= NULL;
dft= xtra= key= fmt= NULL;
v= ' ';
#if defined(NEW_WAY)
rem= "";
......@@ -5407,6 +5425,9 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
if (crp->Kdata->GetIntValue(i))
tm= 0; // Nullable
break;
case FLD_FORMAT:
fmt= (crp->Kdata) ? crp->Kdata->GetCharValue(i) : NULL;
break;
case FLD_REM:
rem= crp->Kdata->GetCharValue(i);
......@@ -5485,7 +5506,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
tm, rem, 0, dbf, v);
#else // !NEW_WAY
if (add_field(&sql, cnm, typ, prec, dec, key, tm, rem, dft, xtra,
0, dbf, v))
fmt, 0, dbf, v))
rc= HA_ERR_OUT_OF_MEM;
#endif // !NEW_WAY
} // endfor i
......@@ -6545,7 +6566,7 @@ maria_declare_plugin(connect)
0x0103, /* version number (1.03) */
NULL, /* status variables */
connect_system_variables, /* system variables */
"1.03.0005", /* string version */
"1.03.0006", /* string version */
MariaDB_PLUGIN_MATURITY_BETA /* maturity */
}
maria_declare_plugin_end;
/* Copyright (C) Olivier Bertrand 2004 - 2014
/* Copyright (C) Olivier Bertrand 2004 - 2015
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -56,11 +56,7 @@ public:
oldopn= newopn= NULL;
oldpix= newpix= NULL;}
inline char *SetName(PGLOBAL g, char *name) {
char *nm= NULL;
if (name) {nm= (char*)PlugSubAlloc(g, NULL, strlen(name) + 1);
strcpy(nm, name);}
return nm;}
inline char *SetName(PGLOBAL g, char *name) {return PlugDup(g, name);}
bool oldsep; // Sepindex before create/alter
bool newsep; // Sepindex after create/alter
......
/*************** json CPP Declares Source Code File (.H) ***************/
/* Name: json.cpp Version 1.0 */
/* Name: json.cpp Version 1.1 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* */
......@@ -1157,6 +1157,6 @@ void JVALUE::SetString(PGLOBAL g, PSZ s)
/***********************************************************************/
bool JVALUE::IsNull(void)
{
return (Jsp) ? Jsp->IsNull() : (Value) ? Value->IsNull() : true;
return (Jsp) ? Jsp->IsNull() : (Value) ? Value->IsZero() : true;
} // end of IsNull
/**************** json H Declares Source Code File (.H) ****************/
/* Name: json.h Version 1.0 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* */
/* This file contains the JSON classes declares. */
/***********************************************************************/
#include "value.h"
#if defined(_DEBUG)
#define X assert(false);
#else
#define X
#endif
enum JTYP {TYPE_STRG = 1,
TYPE_DBL = 2,
TYPE_BOOL = 4,
TYPE_INTG = 7,
TYPE_JSON = 12,
TYPE_JAR,
TYPE_JOB,
TYPE_JVAL};
class JOUT;
class JSON;
class JMAP;
class JVALUE;
class JOBJECT;
class JARRAY;
typedef class JPAIR *PJPR;
typedef class JSON *PJSON;
typedef class JVALUE *PJVAL;
typedef class JOBJECT *PJOB;
typedef class JARRAY *PJAR;
typedef struct {
char *str;
int len;
} STRG, *PSG;
PJSON ParseJson(PGLOBAL g, char *s, int n, int prty, bool *b = NULL);
PJAR ParseArray(PGLOBAL g, int& i, STRG& src);
PJOB ParseObject(PGLOBAL g, int& i, STRG& src);
PJVAL ParseValue(PGLOBAL g, int& i, STRG& src);
char *ParseString(PGLOBAL g, int& i, STRG& src);
PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src);
PSZ Serialize(PGLOBAL g, PJSON jsp, FILE *fs, int pretty);
bool SerializeArray(JOUT *js, PJAR jarp, bool b);
bool SerializeObject(JOUT *js, PJOB jobp);
bool SerializeValue(JOUT *js, PJVAL jvp);
/***********************************************************************/
/* Class JOUT. Used by Serialize. */
/***********************************************************************/
class JOUT : public BLOCK {
public:
JOUT(PGLOBAL gp) : BLOCK() {g = gp;}
virtual bool WriteStr(const char *s) = 0;
virtual bool WriteChr(const char c) = 0;
virtual bool Escape(const char *s) = 0;
// Member
PGLOBAL g;
}; // end of class JOUT
/***********************************************************************/
/* Class JOUTSTR. Used to Serialize to a string. */
/***********************************************************************/
class JOUTSTR : public JOUT {
public:
JOUTSTR(PGLOBAL g);
virtual bool WriteStr(const char *s);
virtual bool WriteChr(const char c);
virtual bool Escape(const char *s);
// Member
char *Strp; // The serialized string
size_t N; // Position of next char
size_t Max; // String max size
}; // end of class JOUTSTR
/***********************************************************************/
/* Class JOUTFILE. Used to Serialize to a file. */
/***********************************************************************/
class JOUTFILE : public JOUT {
public:
JOUTFILE(PGLOBAL g, FILE *str) : JOUT(g) {Stream = str;}
virtual bool WriteStr(const char *s);
virtual bool WriteChr(const char c);
virtual bool Escape(const char *s);
// Member
FILE *Stream;
}; // end of class JOUTFILE
/***********************************************************************/
/* Class JOUTPRT. Used to Serialize to a pretty file. */
/***********************************************************************/
class JOUTPRT : public JOUTFILE {
public:
JOUTPRT(PGLOBAL g, FILE *str) : JOUTFILE(g, str) {M = 0; B = false;}
virtual bool WriteStr(const char *s);
virtual bool WriteChr(const char c);
// Member
int M;
bool B;
}; // end of class JOUTPRT
/***********************************************************************/
/* Class PAIR. The pairs of a json Object. */
/***********************************************************************/
class JPAIR : public BLOCK {
friend class JOBJECT;
friend PJOB ParseObject(PGLOBAL, int&, STRG&);
friend bool SerializeObject(JOUT *, PJOB);
public:
JPAIR(PSZ key) : BLOCK() {Key = key; Val = NULL; Next = NULL;}
protected:
PSZ Key; // This pair key name
PJVAL Val; // To the value of the pair
PJPR Next; // To the next pair
}; // end of class JPAIR
/***********************************************************************/
/* Class JSON. The base class for all other json classes. */
/***********************************************************************/
class JSON : public BLOCK {
public:
JSON(void) {Size = 0;}
int size(void) {return Size;}
virtual void Clear(void) {Size = 0;}
virtual JTYP GetType(void) {return TYPE_JSON;}
virtual JTYP GetValType(void) {X return TYPE_JSON;}
virtual void InitArray(PGLOBAL g) {X}
virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL) {X return NULL;}
virtual PJPR AddPair(PGLOBAL g, PSZ key) {X return NULL;}
virtual PJVAL GetValue(const char *key) {X return NULL;}
virtual PJOB GetObject(void) {X return NULL;}
virtual PJAR GetArray(void) {X return NULL;}
virtual PJVAL GetValue(int i) {X return NULL;}
virtual PVAL GetValue(void) {X return NULL;}
virtual PJSON GetJson(void) {X return NULL;}
virtual int GetInteger(void) {X return 0;}
virtual double GetFloat() {X return 0.0;}
virtual PSZ GetString() {X return NULL;}
virtual PSZ GetText(PGLOBAL g, PSZ text) {X return NULL;}
virtual bool SetValue(PGLOBAL g, PJVAL jvp, int i) {X return true;}
virtual void SetValue(PGLOBAL g, PJVAL jvp, PSZ key) {X}
virtual void SetValue(PVAL valp) {X}
virtual void SetValue(PJSON jsp) {X}
virtual void SetString(PGLOBAL g, PSZ s) {X}
virtual void SetInteger(PGLOBAL g, int n) {X}
virtual void SetFloat(PGLOBAL g, double f) {X}
virtual bool DeleteValue(int i) {X return true;}
virtual bool IsNull(void) {X return true;}
protected:
int Size;
}; // end of class JSON
/***********************************************************************/
/* Class JOBJECT: contains a list of value pairs. */
/***********************************************************************/
class JOBJECT : public JSON {
friend PJOB ParseObject(PGLOBAL, int&, STRG&);
friend bool SerializeObject(JOUT *, PJOB);
public:
JOBJECT(void) : JSON() {First = Last = NULL;}
using JSON::GetValue;
using JSON::SetValue;
virtual void Clear(void) {First = Last = NULL; Size = 0;}
virtual JTYP GetType(void) {return TYPE_JOB;}
virtual PJPR AddPair(PGLOBAL g, PSZ key);
virtual PJOB GetObject(void) {return this;}
virtual PJVAL GetValue(const char* key);
virtual PSZ GetText(PGLOBAL g, PSZ text);
virtual void SetValue(PGLOBAL g, PJVAL jvp, PSZ key);
virtual bool IsNull(void);
protected:
PJPR First;
PJPR Last;
}; // end of class JOBJECT
/***********************************************************************/
/* Class JARRAY. */
/***********************************************************************/
class JARRAY : public JSON {
friend PJAR ParseArray(PGLOBAL, int&, STRG&);
public:
JARRAY(void) : JSON() {Alloc = 0; First = Last = NULL; Mvals = NULL;}
using JSON::GetValue;
using JSON::SetValue;
virtual void Clear(void) {First = Last = NULL; Size = 0;}
virtual JTYP GetType(void) {return TYPE_JAR;}
virtual PJAR GetArray(void) {return this;}
virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL);
virtual void InitArray(PGLOBAL g);
virtual PJVAL GetValue(int i);
virtual bool SetValue(PGLOBAL g, PJVAL jvp, int i);
virtual bool DeleteValue(int n);
virtual bool IsNull(void);
protected:
// Members
int Alloc; // The Mvals allocated size
PJVAL First; // Used when constructing
PJVAL Last; // Last constructed value
PJVAL *Mvals; // Allocated when finished
}; // end of class JARRAY
/***********************************************************************/
/* Class JVALUE. */
/***********************************************************************/
class JVALUE : public JSON {
friend class JARRAY;
friend PJVAL ParseValue(PGLOBAL, int&, STRG&);
friend bool SerializeValue(JOUT *, PJVAL);
public:
JVALUE(void) : JSON()
{Jsp = NULL; Value = NULL; Next = NULL; Del = false;}
JVALUE(PJSON jsp) : JSON()
{Jsp = jsp; Value = NULL; Next = NULL; Del = false;}
JVALUE(PGLOBAL g, PVAL valp);
using JSON::GetValue;
using JSON::SetValue;
virtual void Clear(void)
{Jsp = NULL; Value = NULL; Next = NULL; Del = false; Size = 0;}
virtual JTYP GetType(void) {return TYPE_JVAL;}
virtual JTYP GetValType(void);
virtual PJOB GetObject(void);
virtual PJAR GetArray(void);
virtual PVAL GetValue(void) {return Value;}
virtual PJSON GetJson(void) {return (Jsp ? Jsp : this);}
virtual int GetInteger(void);
virtual double GetFloat(void);
virtual PSZ GetString(void);
virtual PSZ GetText(PGLOBAL g, PSZ text);
virtual void SetValue(PVAL valp) {Value = valp;}
virtual void SetValue(PJSON jsp) {Jsp = jsp;}
virtual void SetString(PGLOBAL g, PSZ s);
virtual void SetInteger(PGLOBAL g, int n);
virtual void SetFloat(PGLOBAL g, double f);
virtual bool IsNull(void);
protected:
PJSON Jsp; // To the json value
PVAL Value; // The numeric value
PJVAL Next; // Next value in array
bool Del; // True when deleted
}; // end of class JVALUE
/**************** json H Declares Source Code File (.H) ****************/
/* Name: json.h Version 1.1 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* */
/* This file contains the JSON classes declares. */
/***********************************************************************/
#include "value.h"
#if defined(_DEBUG)
#define X assert(false);
#else
#define X
#endif
enum JTYP {TYPE_STRG = 1,
TYPE_DBL = 2,
TYPE_BOOL = 4,
TYPE_INTG = 7,
TYPE_JSON = 12,
TYPE_JAR,
TYPE_JOB,
TYPE_JVAL};
class JOUT;
class JSON;
class JMAP;
class JVALUE;
class JOBJECT;
class JARRAY;
typedef class JPAIR *PJPR;
typedef class JSON *PJSON;
typedef class JVALUE *PJVAL;
typedef class JOBJECT *PJOB;
typedef class JARRAY *PJAR;
typedef struct {
char *str;
int len;
} STRG, *PSG;
PJSON ParseJson(PGLOBAL g, char *s, int n, int prty, bool *b = NULL);
PJAR ParseArray(PGLOBAL g, int& i, STRG& src);
PJOB ParseObject(PGLOBAL g, int& i, STRG& src);
PJVAL ParseValue(PGLOBAL g, int& i, STRG& src);
char *ParseString(PGLOBAL g, int& i, STRG& src);
PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src);
PSZ Serialize(PGLOBAL g, PJSON jsp, FILE *fs, int pretty);
bool SerializeArray(JOUT *js, PJAR jarp, bool b);
bool SerializeObject(JOUT *js, PJOB jobp);
bool SerializeValue(JOUT *js, PJVAL jvp);
/***********************************************************************/
/* Class JOUT. Used by Serialize. */
/***********************************************************************/
class JOUT : public BLOCK {
public:
JOUT(PGLOBAL gp) : BLOCK() {g = gp;}
virtual bool WriteStr(const char *s) = 0;
virtual bool WriteChr(const char c) = 0;
virtual bool Escape(const char *s) = 0;
// Member
PGLOBAL g;
}; // end of class JOUT
/***********************************************************************/
/* Class JOUTSTR. Used to Serialize to a string. */
/***********************************************************************/
class JOUTSTR : public JOUT {
public:
JOUTSTR(PGLOBAL g);
virtual bool WriteStr(const char *s);
virtual bool WriteChr(const char c);
virtual bool Escape(const char *s);
// Member
char *Strp; // The serialized string
size_t N; // Position of next char
size_t Max; // String max size
}; // end of class JOUTSTR
/***********************************************************************/
/* Class JOUTFILE. Used to Serialize to a file. */
/***********************************************************************/
class JOUTFILE : public JOUT {
public:
JOUTFILE(PGLOBAL g, FILE *str) : JOUT(g) {Stream = str;}
virtual bool WriteStr(const char *s);
virtual bool WriteChr(const char c);
virtual bool Escape(const char *s);
// Member
FILE *Stream;
}; // end of class JOUTFILE
/***********************************************************************/
/* Class JOUTPRT. Used to Serialize to a pretty file. */
/***********************************************************************/
class JOUTPRT : public JOUTFILE {
public:
JOUTPRT(PGLOBAL g, FILE *str) : JOUTFILE(g, str) {M = 0; B = false;}
virtual bool WriteStr(const char *s);
virtual bool WriteChr(const char c);
// Member
int M;
bool B;
}; // end of class JOUTPRT
/***********************************************************************/
/* Class PAIR. The pairs of a json Object. */
/***********************************************************************/
class JPAIR : public BLOCK {
friend class JOBJECT;
friend PJOB ParseObject(PGLOBAL, int&, STRG&);
friend bool SerializeObject(JOUT *, PJOB);
public:
JPAIR(PSZ key) : BLOCK() {Key = key; Val = NULL; Next = NULL;}
inline PSZ GetKey(void) {return Key;}
inline PJVAL GetVal(void) {return Val;}
inline PJPR GetNext(void) {return Next;}
protected:
PSZ Key; // This pair key name
PJVAL Val; // To the value of the pair
PJPR Next; // To the next pair
}; // end of class JPAIR
/***********************************************************************/
/* Class JSON. The base class for all other json classes. */
/***********************************************************************/
class JSON : public BLOCK {
public:
JSON(void) {Size = 0;}
int size(void) {return Size;}
virtual void Clear(void) {Size = 0;}
virtual JTYP GetType(void) {return TYPE_JSON;}
virtual JTYP GetValType(void) {X return TYPE_JSON;}
virtual void InitArray(PGLOBAL g) {X}
virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL) {X return NULL;}
virtual PJPR AddPair(PGLOBAL g, PSZ key) {X return NULL;}
virtual PJVAL GetValue(const char *key) {X return NULL;}
virtual PJOB GetObject(void) {return NULL;}
virtual PJAR GetArray(void) {return NULL;}
virtual PJVAL GetValue(int i) {X return NULL;}
virtual PVAL GetValue(void) {X return NULL;}
virtual PJSON GetJson(void) {X return NULL;}
virtual PJPR GetFirst(void) {X return NULL;}
virtual int GetInteger(void) {X return 0;}
virtual double GetFloat() {X return 0.0;}
virtual PSZ GetString() {X return NULL;}
virtual PSZ GetText(PGLOBAL g, PSZ text) {X return NULL;}
virtual bool SetValue(PGLOBAL g, PJVAL jvp, int i) {X return true;}
virtual void SetValue(PGLOBAL g, PJVAL jvp, PSZ key) {X}
virtual void SetValue(PVAL valp) {X}
virtual void SetValue(PJSON jsp) {X}
virtual void SetString(PGLOBAL g, PSZ s) {X}
virtual void SetInteger(PGLOBAL g, int n) {X}
virtual void SetFloat(PGLOBAL g, double f) {X}
virtual bool DeleteValue(int i) {X return true;}
virtual bool IsNull(void) {X return true;}
protected:
int Size;
}; // end of class JSON
/***********************************************************************/
/* Class JOBJECT: contains a list of value pairs. */
/***********************************************************************/
class JOBJECT : public JSON {
friend PJOB ParseObject(PGLOBAL, int&, STRG&);
friend bool SerializeObject(JOUT *, PJOB);
public:
JOBJECT(void) : JSON() {First = Last = NULL;}
using JSON::GetValue;
using JSON::SetValue;
virtual void Clear(void) {First = Last = NULL; Size = 0;}
virtual JTYP GetType(void) {return TYPE_JOB;}
virtual PJPR GetFirst(void) {return First;}
virtual PJPR AddPair(PGLOBAL g, PSZ key);
virtual PJOB GetObject(void) {return this;}
virtual PJVAL GetValue(const char* key);
virtual PSZ GetText(PGLOBAL g, PSZ text);
virtual void SetValue(PGLOBAL g, PJVAL jvp, PSZ key);
virtual bool IsNull(void);
protected:
PJPR First;
PJPR Last;
}; // end of class JOBJECT
/***********************************************************************/
/* Class JARRAY. */
/***********************************************************************/
class JARRAY : public JSON {
friend PJAR ParseArray(PGLOBAL, int&, STRG&);
public:
JARRAY(void) : JSON() {Alloc = 0; First = Last = NULL; Mvals = NULL;}
using JSON::GetValue;
using JSON::SetValue;
virtual void Clear(void) {First = Last = NULL; Size = 0;}
virtual JTYP GetType(void) {return TYPE_JAR;}
virtual PJAR GetArray(void) {return this;}
virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL);
virtual void InitArray(PGLOBAL g);
virtual PJVAL GetValue(int i);
virtual bool SetValue(PGLOBAL g, PJVAL jvp, int i);
virtual bool DeleteValue(int n);
virtual bool IsNull(void);
protected:
// Members
int Alloc; // The Mvals allocated size
PJVAL First; // Used when constructing
PJVAL Last; // Last constructed value
PJVAL *Mvals; // Allocated when finished
}; // end of class JARRAY
/***********************************************************************/
/* Class JVALUE. */
/***********************************************************************/
class JVALUE : public JSON {
friend class JARRAY;
friend PJVAL ParseValue(PGLOBAL, int&, STRG&);
friend bool SerializeValue(JOUT *, PJVAL);
public:
JVALUE(void) : JSON()
{Jsp = NULL; Value = NULL; Next = NULL; Del = false;}
JVALUE(PJSON jsp) : JSON()
{Jsp = jsp; Value = NULL; Next = NULL; Del = false;}
JVALUE(PGLOBAL g, PVAL valp);
using JSON::GetValue;
using JSON::SetValue;
virtual void Clear(void)
{Jsp = NULL; Value = NULL; Next = NULL; Del = false; Size = 0;}
virtual JTYP GetType(void) {return TYPE_JVAL;}
virtual JTYP GetValType(void);
virtual PJOB GetObject(void);
virtual PJAR GetArray(void);
virtual PVAL GetValue(void) {return Value;}
virtual PJSON GetJson(void) {return (Jsp ? Jsp : this);}
virtual int GetInteger(void);
virtual double GetFloat(void);
virtual PSZ GetString(void);
virtual PSZ GetText(PGLOBAL g, PSZ text);
virtual void SetValue(PVAL valp) {Value = valp;}
virtual void SetValue(PJSON jsp) {Jsp = jsp;}
virtual void SetString(PGLOBAL g, PSZ s);
virtual void SetInteger(PGLOBAL g, int n);
virtual void SetFloat(PGLOBAL g, double f);
virtual bool IsNull(void);
protected:
PJSON Jsp; // To the json value
PVAL Value; // The numeric value
PJVAL Next; // Next value in array
bool Del; // True when deleted
}; // end of class JVALUE
/******************************************************************/
/* Implementation of XML document processing using libxml2 */
/* Author: Olivier Bertrand 2007-2013 */
/* Author: Olivier Bertrand 2007-2015 */
/******************************************************************/
#include "my_global.h"
#include <string.h>
......@@ -408,8 +408,7 @@ PFBLOCK LIBXMLDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn)
xp->Next = (PX2BLOCK)dup->Openlist;
dup->Openlist = (PFBLOCK)xp;
xp->Type = TYPE_FB_XML2;
xp->Fname = (LPCSTR)PlugSubAlloc(g, NULL, strlen(fn) + 1);
strcpy((char*)xp->Fname, fn);
xp->Fname = (LPCSTR)PlugDup(g, fn);
xp->Count = 1;
xp->Length = (m == MODE_READ) ? 1 : 0;
xp->Retcode = rc;
......
/************ Odbconn C++ Functions Source Code File (.CPP) ************/
/* Name: ODBCONN.CPP Version 2.1 */
/* Name: ODBCONN.CPP Version 2.2 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 1998-2015 */
/* */
......@@ -867,8 +867,7 @@ bool DBX::BuildErrorMessage(ODBConn* pdb, HSTMT hstmt)
for (int i = 0; i < MAX_NUM_OF_MSG
&& (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO)
&& strcmp((char*)state, "00000"); i++) {
m_ErrMsg[i] = (PSZ)PlugSubAlloc(g, NULL, strlen((char*)msg) + 1);
strcpy(m_ErrMsg[i], (char*)msg);
m_ErrMsg[i] = (PSZ)PlugDup(g, (char*)msg);
if (trace)
htrc("%s: %s, Native=%d\n", state, msg, native);
......@@ -882,8 +881,7 @@ bool DBX::BuildErrorMessage(ODBConn* pdb, HSTMT hstmt)
} else {
snprintf((char*)msg, SQL_MAX_MESSAGE_LENGTH + 1, "%s: %s", m_Msg,
MSG(BAD_HANDLE_VAL));
m_ErrMsg[0] = (PSZ)PlugSubAlloc(g, NULL, strlen((char*)msg) + 1);
strcpy(m_ErrMsg[0], (char*)msg);
m_ErrMsg[0] = (PSZ)PlugDup(g, (char*)msg);
if (trace)
htrc("%s: rc=%hd\n", SVP(m_ErrMsg[0]), m_RC);
......@@ -1012,8 +1010,7 @@ PSZ ODBConn::GetStringInfo(ushort infotype)
// *buffer = '\0';
} // endif rc
p = (char *)PlugSubAlloc(m_G, NULL, strlen(buffer) + 1);
strcpy(p, buffer);
p = PlugDup(m_G, buffer);
return p;
} // end of GetStringInfo
......
/************** PlgDBSem H Declares Source Code File (.H) **************/
/* Name: PLGDBSEM.H Version 3.6 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 1998-2014 */
/* (C) Copyright to the author Olivier BERTRAND 1998-2015 */
/* */
/* This file contains the PlugDB++ application type definitions. */
/***********************************************************************/
......@@ -504,9 +504,10 @@ enum XFLD {FLD_NO = 0, /* Not a field definition item */
FLD_EXTRA = 13, /* Field extra info */
FLD_PRIV = 14, /* Field priviledges */
FLD_DATEFMT = 15, /* Field date format */
FLD_CAT = 16, /* Table catalog */
FLD_SCHEM = 17, /* Table schema */
FLD_TABNAME = 18}; /* Column Table name */
FLD_FORMAT = 16, /* Field format */
FLD_CAT = 17, /* Table catalog */
FLD_SCHEM = 18, /* Table schema */
FLD_TABNAME = 19}; /* Column Table name */
/***********************************************************************/
/* Result of last SQL noconv query. */
......@@ -584,6 +585,7 @@ DllExport PCATLG PlgGetCatalog(PGLOBAL g, bool jump = true);
DllExport bool PlgSetXdbPath(PGLOBAL g, PSZ, PSZ, char *, int, char *, int);
DllExport void PlgDBfree(MBLOCK&);
DllExport void *PlgDBSubAlloc(PGLOBAL g, void *memp, size_t size);
DllExport char *PlgDBDup(PGLOBAL g, const char *str);
DllExport void *PlgDBalloc(PGLOBAL, void *, MBLOCK&);
DllExport void *PlgDBrealloc(PGLOBAL, void *, MBLOCK&, size_t);
DllExport void NewPointer(PTABS, void *, void *);
......
......@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
/* (C) Copyright to the author Olivier BERTRAND 1998-2014 */
/* (C) Copyright to the author Olivier BERTRAND 1998-2015 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
......@@ -294,8 +294,7 @@ PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids,
#else // !XMSG
GetRcString(ids + crp->Ncol, cname, sizeof(cname));
#endif // !XMSG
crp->Name = (PSZ)PlugSubAlloc(g, NULL, strlen(cname) + 1);
strcpy(crp->Name, cname);
crp->Name = (PSZ)PlugDup(g, cname);
} else
crp->Name = NULL; // Will be set by caller
......@@ -853,8 +852,7 @@ FILE *PlugOpenFile(PGLOBAL g, LPCSTR fname, LPCSTR ftype)
htrc(" fp=%p\n", fp);
// fname may be in volatile memory such as stack
fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(fname) + 1);
strcpy((char*)fp->Fname, fname);
fp->Fname = PlugDup(g, fname);
fp->Count = 1;
fp->Type = TYPE_FB_FILE;
fp->File = fop;
......@@ -1400,6 +1398,23 @@ void *PlgDBSubAlloc(PGLOBAL g, void *memp, size_t size)
return (memp);
} // end of PlgDBSubAlloc
/***********************************************************************/
/* Program for sub-allocating and copying a string in a storage area. */
/***********************************************************************/
char *PlgDBDup(PGLOBAL g, const char *str)
{
if (str) {
char *sm = (char*)PlgDBSubAlloc(g, NULL, strlen(str) + 1);
if (sm)
strcpy(sm, str);
return sm;
} else
return NULL;
} // end of PlgDBDup
/***********************************************************************/
/* PUTOUT: Plug DB object typing routine. */
/***********************************************************************/
......
......@@ -6,7 +6,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
/* (C) Copyright to the author Olivier BERTRAND 1993-2014 */
/* (C) Copyright to the author Olivier BERTRAND 1993-2015 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
......@@ -228,7 +228,6 @@ BOOL PlugIsAbsolutePath(LPCSTR path)
#endif
}
/***********************************************************************/
/* Set the full path of a file relatively to a given path. */
/* Note: this routine is not really implemented for Unix. */
......@@ -385,8 +384,7 @@ char *PlugReadMessage(PGLOBAL g, int mid, char *m)
err:
if (g) {
// Called by STEP
msg = (char *)PlugSubAlloc(g, NULL, strlen(stmsg) + 1);
strcpy(msg, stmsg);
msg = PlugDup(g, stmsg);
} else // Called by MSG or PlgGetErrorMsg
msg = stmsg;
......@@ -421,8 +419,7 @@ char *PlugGetMessage(PGLOBAL g, int mid)
if (g) {
// Called by STEP
msg = (char *)PlugSubAlloc(g, NULL, strlen(stmsg) + 1);
strcpy(msg, stmsg);
msg = PlugDup(g, stmsg);
} else // Called by MSG or PlgGetErrorMsg
msg = stmsg;
......@@ -536,6 +533,22 @@ void *PlugSubAlloc(PGLOBAL g, void *memp, size_t size)
return (memp);
} /* end of PlugSubAlloc */
/***********************************************************************/
/* Program for sub-allocating and copying a string in a storage area. */
/***********************************************************************/
char *PlugDup(PGLOBAL g, const char *str)
{
if (str) {
char *sm = (char*)PlugSubAlloc(g, NULL, strlen(str) + 1);
strcpy(sm, str);
return sm;
} else
return NULL;
} // end of PlugDup
#if 0
/***********************************************************************/
/* This routine suballocate a copy of the passed string. */
/***********************************************************************/
......@@ -552,6 +565,7 @@ char *PlugDup(PGLOBAL g, const char *str)
return(buf);
} /* end of PlugDup */
#endif // 0
/***********************************************************************/
/* This routine makes a pointer from an offset to a memory pointer. */
......
......@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
/* (C) Copyright to the author Olivier BERTRAND 2004-2014 */
/* (C) Copyright to the author Olivier BERTRAND 2004-2015 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
......@@ -153,10 +153,9 @@ char *RELDEF::GetStringCatInfo(PGLOBAL g, PSZ what, PSZ sdef)
if (s) {
if (!Hc->IsPartitioned() ||
(stricmp(what, "filename") && stricmp(what, "tabname")
&& stricmp(what, "connect"))) {
sval= (char*)PlugSubAlloc(g, NULL, strlen(s) + 1);
strcpy(sval, s);
} else
&& stricmp(what, "connect")))
sval= PlugDup(g, s);
else
sval= s;
} else if (!stricmp(what, "filename")) {
......@@ -213,8 +212,7 @@ bool TABDEF::Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am)
{
int poff = 0;
Name = (PSZ)PlugSubAlloc(g, NULL, strlen(name) + 1);
strcpy(Name, name);
Name = (PSZ)PlugDup(g, name);
Cat = cat;
Hc = ((MYCAT*)cat)->GetHandler();
Catfunc = GetFuncID(GetStringCatInfo(g, "Catfunc", NULL));
......@@ -712,8 +710,7 @@ COLDEF::COLDEF(void) : COLCRT()
/***********************************************************************/
int COLDEF::Define(PGLOBAL g, void *memp, PCOLINFO cfp, int poff)
{
Name = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Name) + 1);
strcpy(Name, cfp->Name);
Name = (PSZ)PlugDup(g, cfp->Name);
if (!(cfp->Flags & U_SPECIAL)) {
Poff = poff;
......@@ -735,22 +732,16 @@ int COLDEF::Define(PGLOBAL g, void *memp, PCOLINFO cfp, int poff)
Key = cfp->Key;
Freq = cfp->Freq;
if (cfp->Remark && *cfp->Remark) {
Desc = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Remark) + 1);
strcpy(Desc, cfp->Remark);
} // endif Remark
if (cfp->Remark && *cfp->Remark)
Desc = (PSZ)PlugDup(g, cfp->Remark);
if (cfp->Datefmt) {
Decode = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Datefmt) + 1);
strcpy(Decode, cfp->Datefmt);
} // endif Datefmt
if (cfp->Datefmt)
Decode = (PSZ)PlugDup(g, cfp->Datefmt);
} // endif special
if (cfp->Fieldfmt) {
Fmt = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Fieldfmt) + 1);
strcpy(Fmt, cfp->Fieldfmt);
} // endif Fieldfmt
if (cfp->Fieldfmt)
Fmt = (PSZ)PlugDup(g, cfp->Fieldfmt);
Flags = cfp->Flags;
return (Flags & (U_VIRTUAL|U_SPECIAL)) ? 0 : Long;
......
/************* tabjson C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: tabjson Version 1.0 */
/* PROGRAM NAME: tabjson Version 1.1 */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* This program are the JSON class DB execution routines. */
/***********************************************************************/
......@@ -31,12 +31,451 @@
#endif // ZIP_SUPPORT
#include "tabmul.h"
#include "checklvl.h"
#include "resource.h"
#include "mycat.h"
/***********************************************************************/
/* This should be an option. */
/***********************************************************************/
#define MAXCOL 200 /* Default max column nb in result */
#define TYPE_UNKNOWN 12 /* Must be greater than other types */
/***********************************************************************/
/* External function. */
/***********************************************************************/
USETEMP UseTemp(void);
/***********************************************************************/
/* Make the document tree from a file. */
/***********************************************************************/
PJSON MakeJsonTree(PGLOBAL g, char *fn, char *objn, int pty,
PJAR& doc, DWORD& drc)
{
char *p, *memory, *objpath, *key;
int len, i = 0;
HANDLE hFile;
MEMMAP mm;
PJSON jsp, top;
PJOB objp = NULL;
PJAR arp = NULL;
PJVAL val = NULL;
/*********************************************************************/
/* Create the mapping file object. */
/*********************************************************************/
hFile = CreateFileMap(g, fn, &mm, MODE_READ, false);
if (hFile == INVALID_HANDLE_VALUE) {
drc = GetLastError();
if (!*g->Message)
sprintf(g->Message, MSG(OPEN_MODE_ERROR), "map", (int)drc, fn);
return NULL;
} // endif hFile
/*********************************************************************/
/* Get the file size (assuming file is smaller than 4 GB) */
/*********************************************************************/
len = mm.lenL;
memory = (char *)mm.memory;
if (!len) { // Empty file
CloseFileHandle(hFile);
CloseMemMap(memory, len);
drc = ENOENT;
return NULL;
} else if (!memory) {
sprintf(g->Message, MSG(MAP_VIEW_ERROR), fn, drc);
CloseFileHandle(hFile);
return NULL;
} // endif Memory
CloseFileHandle(hFile); // Not used anymore
/*********************************************************************/
/* Parse the json file and allocate its tree structure. */
/*********************************************************************/
g->Message[0] = 0;
jsp = top = ParseJson(g, memory, len, pty);
CloseMemMap(memory, len);
drc = EBADF;
if (!jsp && g->Message[0])
return NULL;
objpath = PlugDup(g, objn); // NULL if !objn
/*********************************************************************/
/* Find the table in the tree structure. */
/*********************************************************************/
for (doc = NULL; jsp && objpath; objpath = p) {
if ((p = strchr(objpath, ':')))
*p++ = 0;
if (*objpath != '[') { // objpass is a key
if (jsp->GetType() != TYPE_JOB) {
strcpy(g->Message, "Table path does no match json file");
return NULL;
} // endif Type
key = objpath;
objp = jsp->GetObject();
arp = NULL;
val = objp->GetValue(key);
if (!val || !(jsp = val->GetJson())) {
sprintf(g->Message, "Cannot find object key %s", key);
return NULL;
} // endif val
} else if (objpath[strlen(objpath)-1] == ']') {
if (jsp->GetType() != TYPE_JAR) {
strcpy(g->Message, "Table path does no match json file");
return NULL;
} // endif Type
arp = jsp->GetArray();
objp = NULL;
i = atoi(objpath+1) - 1;
val = arp->GetValue(i);
if (!val) {
sprintf(g->Message, "Cannot find array value %d", i);
return NULL;
} // endif val
} else {
sprintf(g->Message, "Invalid Table path %s", objn);
return NULL;
} // endif objpath
jsp = val->GetJson();
} // endfor objpath
if (jsp && jsp->GetType() == TYPE_JAR)
doc = jsp->GetArray();
else {
// The table is void or is just one object or one value
doc = new(g) JARRAY;
if (val) {
doc->AddValue(g, val);
doc->InitArray(g);
} else if (jsp) {
doc->AddValue(g, new(g) JVALUE(jsp));
doc->InitArray(g);
} // endif val
if (objp)
objp->SetValue(g, new(g) JVALUE(doc), key);
else if (arp)
arp->SetValue(g, new(g) JVALUE(doc), i);
else
top = doc;
} // endif jsp
return top;
} // end of MakeJsonTree
typedef struct _jncol {
struct _jncol *Next;
char *Name;
char *Fmt;
int Type;
int Len;
int Scale;
bool Cbn;
bool Found;
} JCOL, *PJCL;
/***********************************************************************/
/* JSONColumns: construct the result blocks containing the description */
/* of all the columns of a table contained inside a JSON file. */
/***********************************************************************/
PQRYRES JSONColumns(PGLOBAL g, char *dp, const char *fn, char *objn,
int pretty, int lrecl, int lvl, bool info)
{
static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT,
TYPE_INT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING};
static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC,
FLD_LENGTH, FLD_SCALE, FLD_NULL, FLD_FORMAT};
static unsigned int length[] = {0, 6, 8, 10, 10, 6, 6, 0};
char filename[_MAX_PATH], colname[65], fmt[129], *buf;
int i, j, n = 0;
int ncol = sizeof(buftyp) / sizeof(int);
FILE *infile;
DWORD drc;
PVAL valp;
JCOL jcol;
PJCL jcp, fjcp = NULL, pjcp = NULL;
PJPR *jrp, jpp;
PJSON jsp;
PJVAL jvp;
PJOB row;
PJAR doc;
PQRYRES qrp;
PCOLRES crp;
if (info) {
length[0] = 128;
length[7] = 256;
goto skipit;
} // endif info
if (trace)
htrc("File %s pretty=%d lvl=%d lrecl=%d\n",
SVP(fn), pretty, lvl, lrecl);
/*********************************************************************/
/* Open the input file. */
/*********************************************************************/
if (!fn) {
strcpy(g->Message, MSG(MISSING_FNAME));
return NULL;
} else
PlugSetPath(filename, fn, dp);
if (pretty == 2) {
if (!MakeJsonTree(g, filename, objn, pretty, doc, drc))
return NULL;
jsp = (doc) ? doc->GetValue(0) : NULL;
} else {
if (!lrecl) {
sprintf(g->Message, "LRECL must be specified for pretty=%d", pretty);
return NULL;
} else if (!(buf = (char*)PlugSubAlloc(g, NULL, lrecl))) {
sprintf(g->Message, "Alloc error, lrecl=%d", lrecl);
return NULL;
} // endif buf
if (!(infile = global_fopen(g, MSGID_CANNOT_OPEN, filename, "r")))
return NULL;
// Read first record
for (i = 0; i <= pretty; i++)
if (!fgets(buf, lrecl, infile)) {
if (feof(infile)) {
strcpy(g->Message, "Void json table");
return NULL;
} // endif fgets
sprintf(g->Message, MSG(READ_ERROR), filename, strerror(0));
return NULL;
} // endif fgets
jsp = ParseJson(g, buf, strlen(buf), pretty, NULL);
} // endif pretty
if (!(row = (jsp) ? jsp->GetObject() : NULL)) {
strcpy(g->Message, "Can only retrieve columns from object rows");
return NULL;
} // endif row
jcol.Next = NULL;
jcol.Found = true;
colname[64] = 0;
fmt[128] = 0;
jrp = (PJPR*)PlugSubAlloc(g, NULL, sizeof(PJPR) * lvl);
/*********************************************************************/
/* Analyse the JSON tree and define columns. */
/*********************************************************************/
for (i = 1; ; i++) {
for (jpp = row->GetFirst(); jpp; jpp = jpp->GetNext()) {
for (j = 0; j < lvl; j++)
jrp[j] = NULL;
more:
strncpy(colname, jpp->GetKey(), 64);
*fmt = 0;
j = 0;
jvp = jpp->GetVal();
retry:
if ((valp = jvp ? jvp->GetValue() : NULL)) {
jcol.Type = valp->GetType();
jcol.Len = valp->GetValLen();
jcol.Scale = valp->GetValPrec();
jcol.Cbn = valp->IsNull();
} else if (!jvp || jvp->IsNull()) {
jcol.Type = TYPE_UNKNOWN;
jcol.Len = jcol.Scale = 0;
jcol.Cbn = true;
} else if (j < lvl) {
if (!*fmt)
strcpy(fmt, colname);
jsp = jvp->GetJson();
switch (jsp->GetType()) {
case TYPE_JOB:
if (!jrp[j])
jrp[j] = jsp->GetFirst();
strncat(strncat(fmt, ":", 128), jrp[j]->GetKey(), 128);
strncat(strncat(colname, "_", 64), jrp[j]->GetKey(), 64);
jvp = jrp[j]->GetVal();
j++;
break;
case TYPE_JAR:
strncat(fmt, ":", 128);
jvp = jsp->GetValue(0);
break;
default:
sprintf(g->Message, "Logical error after %s", fmt);
goto err;
} // endswitch jsp
goto retry;
} else {
jcol.Type = TYPE_STRING;
jcol.Len = 256;
jcol.Scale = 0;
jcol.Cbn = true;
} // endif's
// Check whether this column was already found
for (jcp = fjcp; jcp; jcp = jcp->Next)
if (!strcmp(colname, jcp->Name))
break;
if (jcp) {
if (jcp->Type != jcol.Type)
jcp->Type = TYPE_STRING;
if (*fmt && (!jcp->Fmt || strlen(jcp->Fmt) < strlen(fmt))) {
jcp->Fmt = PlugDup(g, fmt);
length[7] = MY_MAX(length[7], strlen(fmt));
} // endif *fmt
jcp->Len = MY_MAX(jcp->Len, jcol.Len);
jcp->Scale = MY_MAX(jcp->Scale, jcol.Scale);
jcp->Cbn |= jcol.Cbn;
jcp->Found = true;
} else {
// New column
jcp = (PJCL)PlugSubAlloc(g, NULL, sizeof(JCOL));
*jcp = jcol;
jcp->Cbn |= (i > 1);
jcp->Name = PlugDup(g, colname);
length[0] = MY_MAX(length[0], strlen(colname));
if (*fmt) {
jcp->Fmt = PlugDup(g, fmt);
length[7] = MY_MAX(length[7], strlen(fmt));
} else
jcp->Fmt = NULL;
if (pjcp) {
jcp->Next = pjcp->Next;
pjcp->Next = jcp;
} else
fjcp = jcp;
n++;
} // endif jcp
pjcp = jcp;
for (j = lvl - 1; j >= 0; j--)
if (jrp[j] && (jrp[j] = jrp[j]->GetNext()))
goto more;
} // endfor jpp
// Missing column can be null
for (jcp = fjcp; jcp; jcp = jcp->Next) {
jcp->Cbn |= !jcp->Found;
jcp->Found = false;
} // endfor jcp
if (pretty != 2) {
// Read next record
if (!fgets(buf, lrecl, infile)) {
if (!feof(infile)) {
sprintf(g->Message, MSG(READ_ERROR), filename, strerror(0));
return NULL;
} else
jsp = NULL;
} else if (pretty == 1 && strlen(buf) == 1 && *buf == ']') {
jsp = NULL;
} else
jsp = ParseJson(g, buf, strlen(buf), pretty, NULL);
} else
jsp = doc->GetValue(i);
if (!(row = (jsp) ? jsp->GetObject() : NULL))
break;
} // endor i
if (pretty != 2)
fclose(infile);
skipit:
if (trace)
htrc("CSVColumns: n=%d len=%d\n", n, length[0]);
/*********************************************************************/
/* Allocate the structures used to refer to the result set. */
/*********************************************************************/
qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3,
buftyp, fldtyp, length, false, false);
crp = qrp->Colresp->Next->Next->Next->Next->Next->Next;
crp->Name = "Nullable";
crp->Next->Name = "Jpath";
if (info || !qrp)
return qrp;
qrp->Nblin = n;
/*********************************************************************/
/* Now get the results into blocks. */
/*********************************************************************/
for (i = 0, jcp = fjcp; jcp; i++, jcp = jcp->Next) {
if (jcp->Type == TYPE_UNKNOWN) // Void column
jcp->Type = TYPE_STRING;
crp = qrp->Colresp; // Column Name
crp->Kdata->SetValue(jcp->Name, i);
crp = crp->Next; // Data Type
crp->Kdata->SetValue(jcp->Type, i);
crp = crp->Next; // Type Name
crp->Kdata->SetValue(GetTypeName(jcp->Type), i);
crp = crp->Next; // Precision
crp->Kdata->SetValue(jcp->Len, i);
crp = crp->Next; // Length
crp->Kdata->SetValue(jcp->Len, i);
crp = crp->Next; // Scale (precision)
crp->Kdata->SetValue(jcp->Scale, i);
crp = crp->Next; // Nullable
crp->Kdata->SetValue(jcp->Cbn ? 1 : 0, i);
crp = crp->Next; // Field format
if (crp->Kdata)
crp->Kdata->SetValue(jcp->Fmt, i);
} // endfor i
/*********************************************************************/
/* Return the result pointer. */
/*********************************************************************/
return qrp;
err:
if (pretty != 2)
fclose(infile);
return NULL;
} // end of JSONColumns
/* -------------------------- Class JSONDEF -------------------------- */
JSONDEF::JSONDEF(void)
......@@ -44,7 +483,9 @@ JSONDEF::JSONDEF(void)
Jmode = MODE_OBJECT;
Objname = NULL;
Xcol = NULL;
Pretty = 2;
Limit = 1;
Level = 0;
ReadMode = 0;
} // end of JSONDEF constructor
......@@ -57,6 +498,7 @@ bool JSONDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
Objname = GetStringCatInfo(g, "Object", NULL);
Xcol = GetStringCatInfo(g, "Expand", NULL);
Pretty = GetIntCatInfo("Pretty", 2);
Level = GetIntCatInfo("Level", 0);
Limit = GetIntCatInfo("Limit", 10);
return DOSDEF::DefineAM(g, "DOS", poff);
} // end of DefineAM
......@@ -66,6 +508,9 @@ bool JSONDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
/***********************************************************************/
PTDB JSONDEF::GetTable(PGLOBAL g, MODE m)
{
if (Catfunc == FNC_COL)
return new(g)TDBJCL(this);
PTDBASE tdbp;
PTXF txfp = NULL;
......@@ -403,7 +848,7 @@ bool JSONCOL::CheckExpand(PGLOBAL g, int i, PSZ nm, bool b)
bool JSONCOL::SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm)
{
int n = (int)strlen(p);
bool dg = true;
bool dg = true, b = false;
PJNODE jnp = &Nodes[i];
if (*p) {
......@@ -417,7 +862,8 @@ bool JSONCOL::SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm)
return true;
} // endif p
} // endif *p
} else
b = true;
// To check whether a numeric Rank was specified
for (int k = 0; dg && p[k]; k++)
......@@ -427,13 +873,19 @@ bool JSONCOL::SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm)
// Default specifications
if (CheckExpand(g, i, nm, false))
return true;
else if (jnp->Op != OP_EXP)
if (!Value->IsTypeNum()) {
else if (jnp->Op != OP_EXP) {
if (b) {
// Return 1st value
jnp->Rank = 1;
jnp->Op = OP_EQ;
} else if (!Value->IsTypeNum()) {
jnp->CncVal = AllocateValue(g, (void*)", ", TYPE_STRING);
jnp->Op = OP_CNC;
} else
jnp->Op = OP_ADD;
} // endif OP
} else if (dg) {
if (atoi(p) > 0) {
// Return nth value
......@@ -537,8 +989,7 @@ bool JSONCOL::ParseJpath(PGLOBAL g)
else if (!Jpath)
Jpath = Name;
pbuf = (char*)PlugSubAlloc(g, NULL, strlen(Jpath) + 1);
strcpy(pbuf, Jpath);
pbuf = PlugDup(g, Jpath);
// The Jpath must be analyzed
for (i = 0, p = pbuf; (p = strchr(p, ':')); i++, p++)
......@@ -1083,6 +1534,38 @@ int TDBJSON::MakeNewDoc(PGLOBAL g)
return RC_OK;
} // end of MakeNewDoc
/***********************************************************************/
/* Make the document tree from a file. */
/***********************************************************************/
int TDBJSON::MakeDocument(PGLOBAL g)
{
char filename[_MAX_PATH];
DWORD drc;
if (Done)
return RC_OK;
else
Done = true;
// Now open the JSON file
PlugSetPath(filename, Txfp->To_File, GetPath());
/*********************************************************************/
/* Get top of the parsed tree and the inside table document. */
/*********************************************************************/
Top = MakeJsonTree(g, filename, Objname, Pretty, Doc, drc);
if (!Top) {
if (drc != ENOENT || Mode != MODE_INSERT)
return RC_FX;
return MakeNewDoc(g);
} // endif !Top
return RC_OK;
} // end of MakeDocument
#if 0
/***********************************************************************/
/* Make the document tree from a file. */
/***********************************************************************/
......@@ -1158,11 +1641,7 @@ int TDBJSON::MakeDocument(PGLOBAL g)
if (!jsp && g->Message[0])
return RC_FX;
if (Objname) {
objpath = (char*)PlugSubAlloc(g, NULL, strlen(Objname) + 1);
strcpy(objpath, Objname);
} else
objpath = NULL;
objpath = PlugDup(g, Objname);
/*********************************************************************/
/* Find the table in the tree structure. */
......@@ -1236,6 +1715,7 @@ int TDBJSON::MakeDocument(PGLOBAL g)
return RC_OK;
} // end of MakeDocument
#endif // 0
/***********************************************************************/
/* JSON Cardinality: returns table size in number of rows. */
......@@ -1484,4 +1964,27 @@ void TDBJSON::CloseDB(PGLOBAL g)
} // end of CloseDB
/* -------------------------- End of json --------------------------- */
/* ---------------------------TDBJCL class --------------------------- */
/***********************************************************************/
/* TDBJCL class constructor. */
/***********************************************************************/
TDBJCL::TDBJCL(PJDEF tdp) : TDBCAT(tdp)
{
Fn = tdp->GetFn();
Objn = tdp->Objname;
Pretty = tdp->Pretty;
Lrecl = tdp->Lrecl;
lvl = tdp->Level;
} // end of TDBJCL constructor
/***********************************************************************/
/* GetResult: Get the list the JSON file columns. */
/***********************************************************************/
PQRYRES TDBJCL::GetResult(PGLOBAL g)
{
return JSONColumns(g, ((PTABDEF)To_Def)->GetPath(), Fn, Objn,
Pretty, Lrecl, lvl, false);
} // end of GetResult
/* --------------------------- End of json --------------------------- */
/*************** tabjson H Declares Source Code File (.H) **************/
/* Name: tabjson.h Version 1.0 */
/* Name: tabjson.h Version 1.1 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* */
......@@ -34,6 +34,7 @@ typedef struct _jnode {
class JSONDEF : public DOSDEF { /* Table description */
friend class TDBJSON;
friend class TDBJSN;
friend class TDBJCL;
public:
// Constructor
JSONDEF(void);
......@@ -52,6 +53,7 @@ class JSONDEF : public DOSDEF { /* Table description */
char *Xcol; /* Name of expandable column */
int Limit; /* Limit of multiple values */
int Pretty; /* Depends on file structure */
int Level; /* Used for catalog table */
bool Strict; /* Strict syntax checking */
}; // end of JSONDEF
......@@ -199,3 +201,25 @@ class TDBJSON : public TDBJSN {
bool Done; // True when document parsing is done
bool Changed; // After Update, Insert or Delete
}; // end of class TDBJSON
/***********************************************************************/
/* This is the class declaration for the JSON catalog table. */
/***********************************************************************/
class TDBJCL : public TDBCAT {
public:
// Constructor
TDBJCL(PJDEF tdp);
protected:
// Specific routines
virtual PQRYRES GetResult(PGLOBAL g);
// Members
//char *Dp;
const char *Fn;
char *Objn;
int Pretty;
int Lrecl;
int lvl;
}; // end of class TDBJCL
......@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
/* (C) Copyright to PlugDB Software Development 2003 - 2012 */
/* (C) Copyright to PlugDB Software Development 2003 - 2015 */
/* Author: Olivier BERTRAND */
/* */
/* WHAT THIS PROGRAM DOES: */
......@@ -172,8 +172,7 @@ bool TDBMUL::InitFileNames(PGLOBAL g)
while (n < PFNZ) {
strcat(strcat(strcpy(filename, drive), direc), FileData.cFileName);
pfn[n] = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
strcpy(pfn[n++], filename);
pfn[n++] = PlugDup(g, filename);
if (!FindNextFile(hSearch, &FileData)) {
rc = GetLastError();
......@@ -239,8 +238,7 @@ bool TDBMUL::InitFileNames(PGLOBAL g)
continue; // Not a match
strcat(strcpy(filename, direc), entry->d_name);
pfn[n] = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
strcpy(pfn[n++], filename);
pfn[n] = PlugDup(g, filename);
if (trace)
htrc("Adding pfn[%d] %s\n", n, filename);
......@@ -292,8 +290,7 @@ bool TDBMUL::InitFileNames(PGLOBAL g)
*(++p) = '\0';
// Suballocate the file name
pfn[n] = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
strcpy(pfn[n++], filename);
pfn[n++] = PlugDup(g, filename);
} // endfor n
} // endif Mul
......
/************ TabOccur CPP Declares Source Code File (.CPP) ************/
/* Name: TABOCCUR.CPP Version 1.1 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2013 */
/* (C) Copyright to the author Olivier BERTRAND 2013 - 2015 */
/* */
/* OCCUR: Table that provides a view of a source table where the */
/* contain of several columns of the source table is placed in only */
......@@ -93,8 +93,7 @@ bool OcrColumns(PGLOBAL g, PQRYRES qrp, const char *col,
} // endif col
// Prepare the column list
colist = (char*)PlugSubAlloc(g, NULL, strlen(col) + 1);
strcpy(colist, col);
colist = PlugDup(g, col);
m = PrepareColist(colist);
if ((rk = (rank && *rank))) {
......@@ -191,8 +190,7 @@ bool OcrSrcCols(PGLOBAL g, PQRYRES qrp, const char *col,
} // endif col
// Prepare the column list
colist = (char*)PlugSubAlloc(g, NULL, strlen(col) + 1);
strcpy(colist, col);
colist = PlugDup(g, col);
m = PrepareColist(colist);
if ((rk = (rank && *rank)))
......
......@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */
/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
......@@ -150,11 +150,18 @@ PQRYRES PIVAID::MakePivotColumns(PGLOBAL g)
query = Tabsrc;
// Open a MySQL connection for this table
if (Myc.Open(g, Host, Database, User, Pwd, Port))
return NULL;
else
if (!Myc.Open(g, Host, Database, User, Pwd, Port)) {
b = true;
// Returned values must be in their original character set
if (Myc.ExecSQL(g, "SET character_set_results=NULL", &w) == RC_FX)
goto err;
else
Myc.FreeResult();
} else
return NULL;
// Send the source command to MySQL
if (Myc.ExecSQL(g, query, &w) == RC_FX)
goto err;
......@@ -241,6 +248,10 @@ PQRYRES PIVAID::MakePivotColumns(PGLOBAL g)
} else {
// The query was limited, we must get pivot column values
// Returned values must be in their original character set
// if (Myc.ExecSQL(g, "SET character_set_results=NULL", &w) == RC_FX)
// goto err;
query = (char*)PlugSubAlloc(g, NULL, 0);
sprintf(query, "SELECT DISTINCT `%s` FROM `%s`", Picol, Tabname);
PlugSubAlloc(g, NULL, strlen(query) + 1);
......@@ -284,8 +295,7 @@ PQRYRES PIVAID::MakePivotColumns(PGLOBAL g)
valp->SetValue_pvblk(Rblkp, i);
colname = valp->GetCharString(buf);
crp->Name = (char*)PlugSubAlloc(g, NULL, strlen(colname) + 1);
strcpy(crp->Name, colname);
crp->Name = PlugDup(g, colname);
crp->Flag = 1;
// Add this column
......
/************ Valblk C++ Functions Source Code File (.CPP) *************/
/* Name: VALBLK.CPP Version 2.1 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */
/* */
/* This file contains the VALBLK and derived classes functions. */
/* Second family is VALBLK, representing simple suballocated arrays */
......@@ -1155,10 +1155,9 @@ void STRBLK::SetValue(PVAL valp, int n)
void STRBLK::SetValue(PSZ p, int n)
{
if (p) {
if (!Sorted || !n || !Strp[n-1] || strcmp(p, Strp[n-1])) {
Strp[n] = (PSZ)PlugSubAlloc(Global, NULL, strlen(p) + 1);
strcpy(Strp[n], p);
} else
if (!Sorted || !n || !Strp[n-1] || strcmp(p, Strp[n-1]))
Strp[n] = (PSZ)PlugDup(Global, p);
else
Strp[n] = Strp[n-1];
} else
......
/**************** Table H Declares Source Code File (.H) ***************/
/* Name: TABLE.H Version 2.3 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 1999-2014 */
/* (C) Copyright to the author Olivier BERTRAND 1999-2015 */
/* */
/* This file contains the TBX, OPJOIN and TDB class definitions. */
/***********************************************************************/
......@@ -24,9 +24,7 @@ typedef class CMD *PCMD;
class CMD : public BLOCK {
public:
// Constructor
CMD(PGLOBAL g, char *cmd) {
Cmd = (char*)PlugSubAlloc(g, NULL, strlen(cmd) + 1);
strcpy(Cmd, cmd); Next = NULL; }
CMD(PGLOBAL g, char *cmd) {Cmd = PlugDup(g, cmd); Next = NULL;}
// Members
PCMD Next;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment