|
ODB Engine Sample App
/*© copyright 1996 UserLand Software, Inc. All Rights Reserved.*/
/*
ODB Tester -- Demonstrates the use of Frontier's "ODB Engine" library.
*/
#ifdef __MWERKS__
#if __INTEL__
#define WIN95VERSION
#else
#define MACVERSION
#endif
#endif
#define NEWFILETESTER 1
#define PASCALSTRINGVERSION 1
typedef unsigned char byte, *ptrbyte;
#ifdef MACVERSION
#include <odbEngine.h>
#include <StandardFile.h>
#include <Folders.h>
#endif
#ifdef WIN95VERSION
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <stdio.h>
#include "odbEngine.h"
#define true 1
#define false 0
#define nil NULL
typedef short Boolean;
typedef byte Str255[256];
extern void GetDateTime (long * secs);
extern Handle frontierAlloc (long userSize);
extern long frontierSize (Handle foo);
extern Handle frontierReAlloc(Handle fooIn, long userSize);
#define GetHandleSize(foo) frontierSize(foo)
#define NewHandle(fooSize) frontierAlloc(fooSize)
#define DisposeHandle(foo) GlobalDiscard(foo)
#define SetHandleSize(h,foosize) frontierReAlloc(h,foosize)
#endif
#ifdef MACVERSION
WindowPtr mainwindow = nil; /*the menu sharing test window*/
Str255 windowmessage; /*the message that's displayed in the main window*/
#endif
static void initmacintosh (void) {
#ifdef MACVERSION
/*
the magic stuff that every Macintosh application needs to do
before doing anything else.
*/
short i;
MaxApplZone ();
InitGraf (&qd.thePort);
InitFonts ();
FlushEvents (everyEvent, 0);
InitWindows ();
InitMenus ();
TEInit ();
InitDialogs (nil);
InitCursor ();
for (i = 0; i < 5; i++) { /*register with Multifinder*/
EventRecord ev;
EventAvail (everyEvent, &ev); /*see TN180 -- splash screen*/
} /*for*/
DrawMenuBar ();
#endif
} /*initmacintosh*/
static void copystring (Str255 source, Str255 dest) {
/*
create a copy of source in dest. copy the length byte and
all the characters in the source string.
assume the strings are pascal strings, with the length byte in
the first character of the string.
*/
short i, len;
len = (short) source [0];
for (i = 0; i <= len; i++)
dest [i] = source [i];
} /*copystring*/
static Boolean pushstring (Str255 bssource, Str255 bsdest) {
short lensource = bssource [0];
short lendest = bsdest [0];
char *psource, *pdest;
if ((lensource + lendest) > 255)
return (false);
pdest = (char *) bsdest + (char) lendest + 1;
psource = (char *) bssource + 1;
bsdest [0] += (char) lensource;
while (lensource--) *pdest++ = *psource++;
return (true);
} /*pushstring*/
static Boolean pushlong (long num, Str255 bsdest) {
Str255 bsint;
#ifdef MACVERSION
NumToString (num, bsint);
#endif
#ifdef WIN95VERSION
wsprintf (bsint+1, "%ld", num);
bsint[0] = strlen(bsint+1);
#endif
return (pushstring (bsint, bsdest));
} /*pushlong*/
static Boolean pushtype (OSType mytype, Str255 bsdest) {
#ifdef MACVERSION
Str255 bs;
bs [0] = sizeof (OSType);
BlockMove (&mytype, &bs [1], sizeof (OSType));
return (pushstring (bs, bsdest));
#endif
#ifdef WIN95VERSION
Str255 bs;
_asm
{
mov eax,mytype
bswap eax
mov mytype,eax
}
memmove (bs+1, &mytype, sizeof (OSType));
bs [0] = sizeof (OSType);
return (pushstring (bs, bsdest));
#endif
} /*pushtype*/
static void ellipsize (Str255 s, short width) {
#ifdef MACVERSION
/*
if the string fits inside the given number of pixels, fine -- do nothing
and return.
if not, return a string that does fit, with ellipses representing the
deleted characters. ellipses are generated by pressing option-semicolon.
*/
char len;
short newwidth;
if ((newwidth = StringWidth (s)) <= width) /*nothing to do, the string fits*/
return;
len = s [0]; /* current length in characters*/
width -= CharWidth ('...'); /* subtract width of ellipses*/
do { /*until it fits (or we run out of characters)*/
newwidth -= CharWidth (s [len]);
--len;
} while ((newwidth > width) && (len != 0));
++len; /*make room for the ellipses*/
s [len] = '...';
s [0] = (char) len;
#endif
} /*ellipsize*/
static void centerstring (Rect r, Str255 s) {
#ifdef MACVERSION
/*
draw the string in the current font, size and style, centered inside
the indicated rectangle.
*/
short rh = r.bottom - r.top;
short rw = r.right - r.left;
short h, v;
FontInfo fi;
GetFontInfo (&fi);
ellipsize (s, rw); /*make sure it fits inside the rectangle, width-wise*/
h = r.left + ((rw - StringWidth (s)) / 2);
v = r.top + ((rh - (fi.ascent + fi.descent)) / 2) + fi.ascent;
MoveTo (h, v);
ClipRect (&r);
DrawString (s);
#endif
} /*centerstring*/
static void setfontsizestyle (short fontnum, short fontsize, short fontstyle) {
#ifdef MACVERSION
TextFont (fontnum);
TextSize (fontsize);
TextFace (fontstyle);
#endif
} /*setfontsizestyle*/
#ifdef MACVERSION
static odbBool fileopen (tyfilespec * pfs, short *fnum) {
OSErr ec;
ec = FSpOpenDF (pfs, fsCurPerm, fnum);
return (ec == noErr);
} /*fileopen*/
#endif
static odbBool filecreateandopen (const tyfilespec *fs, OSType creator, OSType filetype, hdlfilenum *fnum) {
#ifdef MACVERSION
OSErr ec;
ec = FSpCreate (fs, creator, filetype, smSystemScript);
if (ec == dupFNErr) {
#ifdef NEWFILETESTER
if (FSpDelete (fs) != noErr)
return (false);
ec = FSpCreate (fs, creator, filetype, smSystemScript);
#else
ec = noErr;
#endif
}
if (ec != noErr)
return (false);
ec = FSpOpenDF (fs, fsRdWrPerm, fnum);
if (ec != noErr) {
FSpDelete (fs);
return (false); /*failed to open the file for writing*/
}
#endif
#ifdef WIN95VERSION
#ifdef NEWFILETESTER
*fnum = CreateFile ("odbTester.root", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_ALWAYS, FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_NORMAL, NULL);
#else
*fnum = CreateFile ("odbTester.root", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_NORMAL, NULL);
#endif
if (*fnum == INVALID_HANDLE_VALUE)
{
printf ("Error opening file.\n");
return (false);
}
#endif
return (true);
} /*filecreateandopen*/
static void fileclose (hdlfilenum fnum) {
#ifdef MACVERSION
if (fnum != 0)
FSClose (fnum);
#endif
#ifdef WIN95VERSION
if (fnum != 0)
CloseHandle (fnum);
#endif
} /*fileclose*/
static odbBool getdesktopspec (Str255 name, tyfilespec *fs) {
#ifdef MACVERSION
short vnum = 0;
long dirid = 0;
OSErr ec = noErr;
// actually, let's leave it in the default directory...
// ec = FindFolder (kOnSystemDisk, kDesktopFolderType, false, &vnum, &dirid);
if (ec == noErr)
ec = FSMakeFSSpec (vnum, dirid, name, fs);
return (ec == noErr);
#endif
#ifdef WIN95VERSION
copystring (name, (*fs).fullSpecifier);
pushstring ("\x01" "\0", (*fs).fullSpecifier);
return (true);
#endif
} /*getdesktopspec*/
static void updatemainwindow (void) {
#ifdef MACVERSION
Rect r;
Str255 s;
r = (*mainwindow).portRect;
EraseRect (&r);
setfontsizestyle (helvetica, 12, 0);
centerstring (r, windowmessage);
NumToString (FreeMem () / 1024, s);
MoveTo (r.left + 3, r.bottom - 3);
setfontsizestyle (geneva, 9, 0);
DrawString (s);
DrawString ("\pK");
#endif
} /*updatemainwindow*/
static void waitseconds (long n) {
#ifdef MACVERSION
long endticks;
endticks = TickCount () + (n * 60);
while (TickCount () < endticks)
SystemTask ();
#endif
#ifdef WIN95VERSION
long endticks;
endticks = gettickcount () + (n * 60);
while (gettickcount () < endticks)
Yield ();
#endif
} /*waitseconds*/
static void setwindowmessage (Str255 message, short wait) {
#ifdef MACVERSION
copystring (message, windowmessage);
SetPort (mainwindow);
updatemainwindow ();
#endif
#ifdef WIN95VERSION
message[message[0]+1]=0;
printf ("%s\n",message+1);
#endif
if (wait > 0)
waitseconds (wait);
} /*setwindowmessage*/
static Boolean initmainwindow (void) {
#ifdef MACVERSION
WindowPtr w;
w = mainwindow = GetNewWindow (128, nil, (WindowPtr) -1);
if (w == nil)
return (false);
ShowWindow (w);
#endif
return (true);
} /*initmainwindow*/
static Boolean stringtotext (Str255 s, Handle *htext) {
long len;
Handle h;
len = s [0];
h = NewHandle (len);
if (h == nil)
return (false);
#ifdef MACVERSION
BlockMove (&s [1], *h, len);
#endif
#ifdef WIN95VERSION
memmove (*h, s+1, len);
#endif
*htext = h;
return (true);
} /*stringtotext*/
static Boolean texttostring (Handle htext, Str255 s) {
long len;
len = GetHandleSize (htext);
if (len > 255)
len = 255;
#ifdef MACVERSION
BlockMove (*htext, &s [1], len);
#endif
#ifdef WIN95VERSION
memmove (s+1, *htext, len);
#endif
s [0] = (char) len;
return (true);
} /*texttostring*/
static void ODBAccessTest (odbRef odb, Str255 bslookup) {
odbValueRecord val;
odbString bs, msg;
OSType type;
long count;
copystring (bslookup, msg);
pushstring ((ptrbyte) "\x03" ": ", msg);
if (odbDefined (odb, bslookup)) {
odbGetType (odb, bslookup, &type);
switch (type) {
case stringvaluetype:
if (odbGetValue (odb, bslookup, &val)) {
texttostring (val.data.stringvalue, bs);
odbDisposeValue (odb, &val);
}
else
odbGetError (bs);
pushstring (bs, msg);
break;
case tablevaluetype:
if (odbCountItems (odb, bslookup, &count)) {
pushlong (count, msg);
pushstring ((ptrbyte) "\x07" " items.", msg);
}
else {
odbGetError (bs);
pushstring (bs, msg);
}
break;
default:
pushstring ((ptrbyte) "\x08" "type is ", msg);
pushtype (type, msg);
break;
}
}
else
pushstring ((ptrbyte) "\x1A" "The value was not defined.", msg);
setwindowmessage (msg, 0);
} /*ODBAccessTest*/
#ifdef MACVERSION
static void handledrag (EventRecord ev, WindowPtr w) {
Rect r;
r = qd.screenBits.bounds;
r.top = r.top + GetMBarHeight ();
r.left = r.left + 4;
r.right = r.right - 4;
r.bottom = r.bottom - 4;
DragWindow (w, ev.where, &r);
} /*handledrag*/
#endif
static Boolean exitprogram (void) {
#ifdef MACVERSION
/*
returns true if the user clicks in the go-away box.
*/
EventRecord ev;
WindowPtr w;
if (!WaitNextEvent (everyEvent, &ev, 1, nil))
return (false);
if (ev.what != mouseDown)
return (false);
switch (FindWindow (ev.where, &w)) {
case inGoAway: /*click in go-away box to exit program*/
return (TrackGoAway (w, ev.where));
case inSysWindow:
SystemClick (&ev, w);
return (false);
case inDrag:
handledrag (ev, w);
return (false);
} /*switch*/
#endif
return (false); /*don't exit the program*/
} /*exitprogram*/
static unsigned char * paths [] = {
(ptrbyte) "\x0A" "scratchpad",
(ptrbyte) "\x18" "scratchpad.odbTestString",
(ptrbyte) "\x16" "scratchpad.odbTestTime",
(ptrbyte) "\x04" "user",
(ptrbyte) "\x10" "user.preferences",
(ptrbyte) "\x1C" "user.preferences.odbTestSpec",
(ptrbyte) "\x1F" "user.preferences.odbTestStrings",
nil
};
void main (void) {
Boolean error = false;
tyfilespec fs;
short i;
unsigned char **s;
unsigned long ticks;
unsigned long date;
Str255 msg;
Str255 address;
odbValueRecord val;
hdlfilenum fnum = (hdlfilenum)-1;
odbRef odb = nil;
static byte testString [] = "\x29" "this is a test of the ODB Engine library.";
initmacintosh ();
initmainwindow ();
getdesktopspec ((ptrbyte) "\x0E" "odbTester.root", &fs);
if (!filecreateandopen (&fs, 'LAND', 'TABL', &fnum)) {
setwindowmessage ((ptrbyte) "\x20" "Error creating new database file", 1);
goto error;
}
#ifdef NEWFILETESTER
if (!odbNewFile (fnum)) {
setwindowmessage ((ptrbyte) "\x1B" "Error creating new database", 1);
goto error;
}
#endif
if (!odbOpenFile (fnum, &odb)) {
setwindowmessage ((ptrbyte) "\x16" "Error opening database", 1);
goto error;
}
#ifdef NEWFILETESTER
/*start by creating a few tables in the new database*/
odbNewTable (odb, paths [0]);
odbNewTable (odb, paths [3]);
odbNewTable (odb, paths [4]);
odbNewTable (odb, paths [6]);
/*create a string value*/
val.valuetype = stringvaluetype;
#ifdef MACVERSION
PtrToHand (testString+1, &val.data.stringvalue, *testString);
#endif
#ifdef WIN95VERSION
val.data.stringvalue = GlobalAlloc(GMEM_MOVEABLE, testString [0]+2);
memmove (*(val.data.stringvalue), testString+1, testString [0]);
#endif
odbSetValue (odb, paths [1], &val);
/*add a bunch of strings to odbTestStrings*/
setwindowmessage ((ptrbyte) "\x3B" "add 100 testing strings to user.preferences.odbTestStrings.", 2);
for (i = 1; i <= 100; i++) {
copystring (paths [6], address);
pushstring ((ptrbyte) "\x07" ".string", address);
pushlong (i, address);
odbSetValue (odb, address, &val);
} /*for*/
odbDisposeValue (odb, &val);
/*create a date value*/
val.valuetype = datevaluetype;
GetDateTime (&val.data.datevalue);
odbSetValue (odb, paths [2], &val);
/*create a filespec value*/
#ifdef MACVERSION
val.valuetype = filespecvaluetype;
PtrToHand (&fs, (Handle *) &val.data.filespecvalue, sizeof (fs));
odbSetValue (odb, paths [5], &val);
odbDisposeValue (odb, &val);
#endif
/*first, test reading slowly*/
#endif
for (s = paths; *s != nil; s++) {
if (exitprogram ()) /*returns true if user clicks in go away box*/
return;
ODBAccessTest (odb, *s);
waitseconds (1); /*give user more time to see the result*/
} /*for*/
/*now, test fast*/
ticks = gettickcount ();
for (i = 1; i <= 20; i++) {
for (s = paths; *s != nil; s++) {
if (exitprogram ()) /*returns true if user clicks in go away box*/
return;
ODBAccessTest (odb, *s);
} /*for*/
} /*for*/
/*report statistics*/ {
ticks = gettickcount () - ticks;
copystring ((ptrbyte) "\x10" "140 accesses in ", msg);
pushlong (ticks, msg);
pushstring ((ptrbyte) "\x07" " ticks.", msg);
setwindowmessage (msg, 2);
}
/*now let's delete a value we added*/
odbDelete (odb, paths [1]);
/*change an existing value*/
val.valuetype = datevaluetype;
GetDateTime (&val.data.datevalue);
odbSetValue (odb, paths [2], &val);
/* check that the mod date changed*/
odbGetModDate (odb, (ptrbyte) "\x0A" "scratchpad", &date);
if (date >= val.data.datevalue)
setwindowmessage ((ptrbyte) "\x15" "mod date was updated.", 2);
else
setwindowmessage ((ptrbyte) "\x19" "mod date was not updated.", 1);
/*finally, save changes*/
odbSaveFile (odb);
setwindowmessage ((ptrbyte) "\x0F" "database saved.", 2);
error:
odbCloseFile (odb);
fileclose (fnum);
} /*main*/
Notes ODB Tester is a simple program illustrating the use of the ODB Engine library. It creates a database file, adds three tables, and puts three data items to those tables. It then accesses the values and displays information about them: For string values, the program displays the string. For table values, the number of items is displayed. For other value, the type is displayed. After cycling through the six values, a tight loop is entered to provide rough timing statistics. You can Quit the ODB Tester before completion by closing its status window. |