<*>= #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include "pushchar.h" #include "pdblua.h" #include "pdbio.h" #include "pdbrep.h" #include "pdbconv.h" <type and macro definitions> <functions>
<type and macro definitions>= (<-U) [D->] #define CURRENT_VERSION 452 #define MAX_FIND_STRING 16 #define MAX_PASSWORD_LENGTH 10 #define MAX_FIELDS 20 #define MAX_FIELD_NAME_LENGTH 20 #define MAX_DATA_LENGTH 500 #define MAX_TOTAL_POPUP_LENGTH 4000 #define MAX_RECORDS 50000 #define MAX_RECORD_LENGTH 4000 typedef enum fieldtype { FLDTYPE_STRING = 0x0001, FLDTYPE_BOOLEAN = 0x0002, FLDTYPE_DATE = 0x0004, FLDTYPE_INT = 0x0008, FLDTYPE_FLOAT = 0x0010, FLDTYPE_TIME = 0x0020, FLDTYPE_LIST = 0x0040 } Fieldtype; static char *typestring(Fieldtype t);
DefinesCURRENT_VERSION
,Fieldtype
,MAX_DATA_LENGTH
,MAX_FIELD_NAME_LENGTH
,MAX_FIELDS
,MAX_FIND_STRING
,MAX_PASSWORD_LENGTH
,MAX_RECORD_LENGTH
,MAX_RECORDS
,MAX_TOTAL_POPUP_LENGTH
,typestring
(links are to index).
String format for types. These should be used on input, too, and they aren't. Bad dog.
<functions>= (<-U) [D->] static char *typestring(Fieldtype t) { switch (t) { case FLDTYPE_STRING: return "string"; case FLDTYPE_BOOLEAN: return "boolean"; case FLDTYPE_DATE: return "date"; case FLDTYPE_INT: return "int"; case FLDTYPE_FLOAT: return "float"; case FLDTYPE_TIME: return "time"; case FLDTYPE_LIST: return "popup"; default: return "unknown"; } }
Definestypestring
(links are to index).
<type and macro definitions>+= (<-U) [<-D] typedef struct { char fieldNames[MAX_FIELDS][MAX_FIELD_NAME_LENGTH+1]; short fieldTypes[MAX_FIELDS]; /* values are the FLDTYPE_... defines above */ short numFields; short version; /* should be equal to the CURRENT_VERSION above */ short showDBColumnWidths[MAX_FIELDS]; /* width in pixels to display for each column */ short showDataWidth; /* width in pixels of the data area when editing a record */ short sort1Field; /* which fields were last chosen to sort on */ short sort2Field; /* secondary... */ short sort3Field; /* tertiary.... */ short findField; /* which field was last 'Find' */ short filterField; /* which field was last 'Filter' */ char findString[MAX_FIND_STRING]; /* the previous 'Find' string */ char filterString[MAX_FIND_STRING]; /* the previous 'Filter' string */ short lockPWOnExit; /* 1 if auto-lock a password'ed db is on, 0 otherwise */ short firstColumnToShow; /* which field to show as the first field */ char password[MAX_PASSWORD_LENGTH+2]; /* password for this db, or the empty string */ char* popupLists; } JFileAppInfoType;
DefinesJFileAppInfoType
(links are to index).
<functions>+= (<-U) [<-D->] static JFileAppInfoType readinfo(Text *t) { JFileAppInfoType info; int i; get_chars(info.fieldNames, t, sizeof(info.fieldNames)); for (i=0; i < MAX_FIELDS; i++) info.fieldTypes[i] = get_short(t); info.numFields = get_short(t); info.version = get_short(t); for (i=0; i < MAX_FIELDS; i++) info.showDBColumnWidths[i] = get_short(t); info.showDataWidth = get_short(t); info.sort1Field = get_short(t); info.sort2Field = get_short(t); info.sort3Field = get_short(t); info.findField = get_short(t); info.filterField = get_short(t); get_chars(info.findString, t, sizeof(info.findString)); get_chars(info.filterString, t, sizeof(info.filterString)); info.lockPWOnExit = get_short(t); info.firstColumnToShow = get_short(t); get_chars(info.password, t, sizeof(info.password)); info.popupLists = (char *)t->p; return info; }
Definesreadinfo
(links are to index).
"popup
X"
, where X is a,b,c.... for field item 1,2,3... etc. The
list must be terminated
with two null characters in a row, and must be shorter than 4000 chars in length.
Example:
"popupb\0First 2\0Second 2\0popupd\0First 4\0Second4\0Third 4\0\0"
The above string would translate to the following popup lists in JFile:
For the second field (ie field 'b'):
First 2 Second 2
For the fourth field (ie field 'd'):
First 4 Second 4 Third 4
Note that the app info structure should not be saving a pointer to the field at all,
it should save the string above as an example - this means that when creating the .pdb
file it is necessary to compute the size of the required appinfo block as the program
runs if you are handling conversion TO JFile .pdb format. If no popup lists are to be
defined, just save 4 '\0'
characters into the space alloted for the popupLists.
Reading in popup lists is easy; just wait for the two nulls.
Read in .pdb and write Lua. I include code to convert to CSV if desired. This is another monster that should be split.
<functions>+= (<-U) [<-D->]
static void dumpinfo(FILE *out, struct pdb *db, Text txt) {
int i;
Text *t = &txt;
JFileAppInfoType info = readinfo(t);
fprintf(out, "\n {\n");
fprintf(out, " -- standard relation between types and typenums\n");
fprintf(out, " Typemap = {\n ");
#define DUMP(X) fprintf(out, "%s = %d, ", typestring(FLDTYPE_ ## X), FLDTYPE_ ## X);
DUMP(STRING) DUMP(BOOLEAN) DUMP(DATE) DUMP(INT) DUMP(FLOAT) DUMP(TIME)
fprintf(out, "%s = %d\n },\n", typestring(FLDTYPE_LIST), FLDTYPE_LIST);
fprintf(out, " version = %d,\n", info.version);
fprintf(out, " fields = {\n");
lua_beginblock();
for(i=0; i<info.numFields; i++) {
fprintf(out, " { type = \"%s\", typenum = %d, width = %d, name = %s",
typestring(info.fieldTypes[i]),
info.fieldTypes[i], info.showDBColumnWidths[i],
QUOTE(info.fieldNames[i]));
<possibly print popups for field i
>
fprintf(out, " }%s\n", comma(i < info.numFields));
}
lua_endblock();
fprintf(out, " },\n dataWidth = %d, firstColumnToShow = %d,\n",
info.showDataWidth, info.firstColumnToShow);
fprintf(out, " findField = %d, filterField = %d,\n",
info.findField, info.filterField);
fprintf(out, " findString = %s, filterString = %s,\n",
quote(info.findString, 1), quote(info.filterString, 1));
fprintf(out, " sortFields = { %d, %d, %d },\n",
info.sort1Field, info.sort2Field, info.sort3Field);
fprintf(out, " password = %s, lock = %d,\n", quote(info.password, 1),
info.lockPWOnExit);
fprintf(out, " }");
}
DefinesDUMP
,dumpinfo
(links are to index).
<possibly print popups for field i
>= (<-U)
{ char *p;
for(p = info.popupLists; strlen(p) != 0; p += strlen(p)+1)
if (!strncmp(p, "popup", 5) && strlen(p) == 6 && p[5] == 'a' + i)
break;
if (strlen(p)) {
lua_beginblock();
fprintf(out, ",\n popups = { ");
for (p += strlen(p)+1; strlen(p) && !(!strncmp(p, "popup", 5) && strlen(p) == 6);){
fprintf(out, "%s", QUOTE(p));
p += strlen(p)+1;
fprintf(out, "%s", strlen(p) == 0 || !strncmp(p, "popup", 5) ? "" : ", ");
}
fprintf(out, " },\n ");
lua_endblock();
}
}
<functions>+= (<-U) [<-D->] static char *infodocs[] = { "`version' is required.", "The `fields' array is required, and every element of it must be a", "table or a string. (If a string, it stands for a field with that", "name and all else defaulted.)", "The table doesn't have to contain anything, but 'name' is", "highly recommended. typenum has priority over type if both are", "present; missings types are strings. The type field can only be", "interpreted if the Typemap array is present. ", "Everything else besides `fields' and `version' can be defaulted.", 0 };
Definesinfodocs
(links are to index).
<functions>+= (<-U) [<-D->] static int WalkLuaPopups(FILE *fp, int nfields); static int pack(FILE *pdb, int appinfo) { JFileAppInfoType info; int nfields; memset(&info, 0, sizeof(info)); if (lua_dostring("info = Database.appInfo")) assert(0); if (lua_isnil(objectval("info.fields"))) { fprintf(stderr, "appInfo for JFile must have `fields'\n"); exit(1); } nfields = intval("listlength(info.fields)"); if (pdb) { int i; <set the fields of theinfo
structure> <write theinfo
structure> } return sizeof(info) - 4 + WalkLuaPopups(pdb, nfields); }
Definespack
(links are to index).
<set the fields of the info
structure>= (<-U)
for (i = 0; i < MAX_FIELDS; i++) {
lua_beginblock();
lua_pushnumber((double)(i+1));
lua_setglobal("i");
if (lua_dostring("if type(info.fields[i]) == 'string' then "
" info.fields[i] = { name = info.fields[i] } "
"end"))
assert(0);
setblock(info.fieldNames[i],
"(info.fields[i] and (info.fields[i].name or ('Field ' .. i))) or ''");
info.fieldTypes[i] = intopt("info.fields[i] and (info.fields[i].typenum or "
"(info.fields[i].type and info.Typemap and"
" info.Typemap[info.fields[i].type]))",
FLDTYPE_STRING);
info.showDBColumnWidths[i] = intopt("info.fields[i] and info.fields[i].width", 80);
lua_endblock();
}
info.numFields = intval("listlength(info.fields)");
info.version = intval("info.version");
info.showDataWidth = intopt("info.dataWidth", 80);
info.sort1Field = intopt("info.sortFields and info.sortFields[1]", 0);
info.sort2Field = intopt("info.sortFields and info.sortFields[2]", 0);
info.sort3Field = intopt("info.sortFields and info.sortFields[3]", 0);
info.findField = intopt("info.findField", 0);
info.filterField = intopt("info.filterField", 0);
setblock(info.findString, "info.findString or ''");
setblock(info.filterString, "info.filterString or ''");
info.lockPWOnExit = intopt("info.lock", 0);
info.firstColumnToShow = intopt("info.firstColumnToShow", 1);
setstringopt(info.password, "info.password", "", MAX_PASSWORD_LENGTH);
<write the info
structure>= (<-U)
put_block(pdb, info.fieldNames);
for (i = 0; i < MAX_FIELDS; i++)
put_short(pdb, info.fieldTypes[i]);
put_short(pdb, info.numFields);
put_short(pdb, info.version);
for (i = 0; i < MAX_FIELDS; i++)
put_short(pdb, info.showDBColumnWidths[i]);
put_short(pdb, info.showDataWidth);
put_short(pdb, info.sort1Field);
put_short(pdb, info.sort2Field);
put_short(pdb, info.sort3Field);
put_short(pdb, info.findField);
put_short(pdb, info.filterField);
put_block(pdb, info.findString);
put_block(pdb, info.filterString);
put_short(pdb, info.lockPWOnExit);
put_short(pdb, info.firstColumnToShow);
put_block(pdb, info.password);
"popup
X"
, where X is a,b,c.... for field item 1,2,3... etc. The
list must be terminated
with two null characters in a row, and must be shorter than 4000 chars in length.
Example:
"popupb\0First 2\0Second 2\0popupd\0First 4\0Second4\0Third 4\0\0"
The above string would translate to the following popup lists in JFile:
For the second field (ie field 'b'):
First 2 Second 2
For the fourth field (ie field 'd'):
First 4 Second 4 Third 4
Note that the app info structure should not be saving a pointer to the field at all,
it should save the string above as an example - this means that when creating the .pdb
file it is necessary to compute the size of the required appinfo block as the program
runs if you are handling conversion TO JFile .pdb format. If no popup lists are to be
defined, just save 4 '\0'
characters into the space alloted for the popupLists.
The following function both computes the size of the popup lists and
(if its argument is non-null) emits them.
We need the size first in order to compute the size of the app-info
area, which we need in order to compute the record offsets, which have
to be written first.
Therefore this function is always called exactly twice: once with
NULL
, and once with the output file.
<functions>+= (<-U) [<-D->] static int WalkLuaPopups(FILE *fp, int nfields) { int i, j; int len = 0; lua_beginblock(); for (i = 0; i < nfields; i++) { lua_pushnumber((double)(i+1)); lua_setglobal("i"); if (!lua_istable(objectval("Database.appInfo.fields[i]")) || !lua_istable(objectval("Database.appInfo.fields[i].popups"))) continue; len += strlen("popupX") + 1; if (fp) fprintf(fp, "popup%c%c", 'a' + i, 0); lua_beginblock(); for (j = 1; ; j++) { lua_Object item; lua_pushnumber((double)j); lua_setglobal("j"); item = objectval("Database.appInfo.fields[i].popups[j]"); if (!lua_isstring(item)) break; len += strlen(lua_getstring(item)) + 1; if (fp) (void)fprintf(fp, "%s%c", lua_getstring(item), 0); } lua_endblock(); } lua_endblock(); len++; if (fp) (void)putc(0, fp); while (len < 4) { len++; if (fp) (void)putc(0, fp); } return len; }
DefinesWalkLuaPopups
(links are to index).
<functions>+= (<-U) [<-D->] static char *trailers[] = { "-- take a Database and write comma-separated values", "-- note this doesn't use C-like quoting conventions", "function writecsv(t)", " local fields, records", " i = 1", " while t.appInfo.fields[i] do", " if i > 1 then write(',') end", " write(csvquote(t.appInfo.fields[i].name))", " i = i + 1", " end", " write('\\n')", "", " j = 1", " while t.records[j] do", " i = 1", " while t.records[j].data[i] do", " if i > 1 then write(',') end", " write(csvquote(t.records[j].data[i]))", " i = i + 1", " end", " write('\\n')", " j = j + 1", " end", "end", "", "function csvquote(s)", " -- convert newlines to blanks and repeat double quotes", " return '\"' .. gsub(gsub(s, '\"', '\"\"'), '[\\n\\r]', ' ') .. '\"'", "end", (char *)0 };
Definestrailers
(links are to index).
<functions>+= (<-U) [<-D] struct PDBConv JFile = { "JFile database", "JbDb", "JBas", { infodocs, dumpinfo, pack }, { 0, generic_unpackInfo, generic_packInfo }, { 0, generic_unpackRecord, generic_packRecord }, trailers };
i
>: U1, D2
info
structure>: U1, D2
info
structure>: U1, D2