************************************* * * * DB/C Newsletter * * February 1997 * * * ************************************* Editor's Notes The DB/C File System Server (FSS) 1.0 was released in January. The response has been very favorable. Marketing information about FSS is available at www.swc.com. DB/C 9.1 beta testing continues. You can get a copy of the latest beta test version of DB/C 9.1 at ftp.swc.com in directory /pub/misc/dbc91tst. If you don't already have the DB/C Newsletter delivered to your email address and would like to have it emailed to you once a month, just send an email message to 'majordomo@swc.com' and put the line 'subscribe dbcnews' in the body of the email message (omit the ' characters). The newsletter will be delivered to the email address from which the message was sent. This month's newsletter provides a complete working example of a CVERB, including the C program that implements the functionality of the CVERB. Let me know if you find it helpful. don.wills@swc.com A CVERB Example DB/C is a great language for writing business applications. DB/C's portability is unsurpassed. However, because of the portability issues, many operating system specific functions are not available as DB/C verbs. When you need to do an operating system level operation, you need to use the CCALL or CVERB statements of DB/C. These statements give the programmer the ability to call any operating system function and return information to the DB/C program. The only gotcha is that you need to write some code in C. The C Interface chapter (Chapter 45) in the DB/C 9.0 Programmer's Reference provides the information required to write a C program that interfaces with DB/C. In Windows 95/NT, there is an additional wrinkle because the C code is in a separate DLL instead of being statically linked with the DB/C runtime. The wrinkle is that Windows provides for entry points into a DLL, but does not provide for entry points into an executable file (e.g. DBCC.EXE). DB/C for Windows 95/NT gets around this problem by calling a function named cverbinit in the DLL. The one parameter to this function is a pointer to an array of pointers to the functions inside the DB/C runtime (e.g. dbcseteos()). By using the cverbinit entry point and by using #defines, the C code can be written without regard for all of this. The C code that handles this is found in cvrbinit.h. The rest of this article consists of an example of the CVERB feature of DB/C in action. The first program is the DB/C program and the second program is the C program. All of this code and the actual DLL itself can be found in the /pub/misc/cverb directory at ftp.swc.com. The example shows CVERBs with these capabilities: 1. A DIR statement that returns an array of file names that match a lookup specification. 2. A FILESIZE statement that returns the size of a file. 3. A FILECREATETIME statement that returns the file's creation time/date. 4. A FILEWRITETIME statement that returns the file's last update time/date. 5. A FILEWRITETIME statement that returns the file's last access time/date. Here is an example DB/C program that uses these statements: * * demonstration of cverb * use in conjunction with democvrb.c on Windows 95/NT * dir cverb #cvarlit, #carray filesize cverb #cvarlit, #cvar filecreatetime cverb #cvarlit, #cvar filewritetime cverb #cvarlit, #cvar filereadtime cverb #cvarlit, #cvar resulta char 50[50] result char 100 n int 3 display *es, "Demonstration of cverbs" * * DIR * display "dir *.*" dir "*.*", resulta if over display " NO MATCHING FILES" else for n from 1 to 50 if (formptr resulta[n]) display " ", resulta[n] endif repeat endif * * FILESIZE * display "filesize democvrb.prg" filesize "democvrb.prg", result if over display " File not found" else display " ", result endif * * FILECREATETIME * display "filecreatetime democvrb.prg" filecreatetime "democvrb.prg", result if over display " File not found" else display " ", result endif * * FILEWRITETIME * display "filewritetime democvrb.prg" filewritetime "democvrb.prg", result if over display " File not found" else display " ", result endif * * FILEREADTIME * display "filereadtime democvrb.prg" filereadtime "democvrb.prg", result if over display " File not found" else display " ", result endif keyin "Tap enter to end", result * * end of example DB/C program * The C code to support this is compiled and linked into a file called DBCCVRB.DLL. Here is this C code: // Sample cverb DLL // general format of the cverbs supported by this DLL is: // , // may be: DIR, FILESIZE, FILECREATETIME, FILEWRITETIME, FILEREADTIME // for DIR, is a character variable or literal that // contains a filename that may include wildcards // is a character array variable that will // receive the filenames that match // for all of the others, is a character variable or // literal that contains a filename // is a character variable to contain the result // if the input filename does not exist, the formpointer of the // output variable will be set to 0 // for FILESIZE, the result is a 12 digit value that is the file size, // blank filled on the left // for the others, result is a 16 digit timestamp in the form: // yyyymmddhhmmsspp (pp is hundredths of a second) // for FILECREATETIME, the time is when the file was created // for FILEWRITETIME, the time is the time that the file was last written // for FILEREADTIME, the time is the time the file was last accessed // // the OVER flag is set if no file is found, otherwise it is cleared #include #include #include #define V_DIR 1 #define V_SIZE 2 #define V_CREATE 3 #define V_WRITE 4 #define V_READ 5 static int cvarlittos(unsigned char *, char *); static void stocvar(char *, unsigned char *); static void itossize(int, char *, int); #include "cvrbinit.h" __declspec(dllexport) void cverb(name, list) unsigned char *name, **list; { int i1, i2, verb, arrayincr; unsigned char *input, *output, *lastoutput; char cmd[20], workstring[256]; WIN32_FIND_DATA finddata; HANDLE findhandle; FILETIME ft; SYSTEMTIME st; // check parameters dbcsetover(1); if (name == NULL || list == NULL) return; input = *list++; output = *list; if (input == NULL || output == NULL) return; // convert input var or lit to string if (cvarlittos(input, workstring)) return; // parse verb and call appropriate function i1 = strlen((CHAR *) name); while (i1 && name[i1 - 1] == ' ') i1--; if (!i1) return; name[i1] = 0; for (i1 = 0; name[i1] == ' '; i1++); for (i2 = 0; name[i1] && i2 < 20; i1++, i2++) cmd[i2] = toupper(name[i1]); if (i2 == 20) return; cmd[i2] = 0; if (!strcmp(cmd, "DIR")) verb = V_DIR; else if (!strcmp(cmd, "FILESIZE")) verb = V_SIZE; else if (!strcmp(cmd, "FILECREATETIME")) verb = V_CREATE; else if (!strcmp(cmd, "FILEWRITETIME")) verb = V_WRITE; else if (!strcmp(cmd, "FILEREADTIME")) verb = V_READ; else return; // for DIR command, make sure output variable is an array of char vars // set output to point to first char var in array // set lastoutput to point to last char var in array // set arrayincr to be number of bytes to increment output if (verb == V_DIR) { if (output[0] == 0xa6) { // one dim array i1 = output[1] + (output[2] << 8); output += 3; } else if (output[0] == 0xa7) { // two dim array i1 = output[1] + (output[2] << 8); i1 *= (output[3] + (output[4] << 8)); output += 5; } else if (output[0] == 0xa8) { // three dim array i1 = output[1] + (output[2] << 8); i1 *= (output[3] + (output[4] << 8)); i1 *= (output[5] + (output[6] << 8)); output += 7; } else return; // not an array arrayincr = output[0] + (output[1] << 8); output += 2; lastoutput = output + arrayincr * (i1 - 1); if (output[0] & 0x80 && output[0] != 0xF0) return; // not char var // FindFirstFile and FindNextFile are used to retrieve files // put each result into destination array // put zero length strings in all remaining variables of array findhandle = FindFirstFile(workstring, &finddata); if (findhandle != INVALID_HANDLE_VALUE) { while (output <= lastoutput) { if (!(finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !(finddata.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) { stocvar(finddata.cFileName, output); output += arrayincr; } if (!FindNextFile(findhandle, &finddata)) break; } FindClose(findhandle); } while (output <= lastoutput) { stocvar("", output); output += arrayincr; } dbcsetover(0); return; } // for all others, check to see that output variable is char var // use FindFirstFile to get information if (output[0] & 0x80 && output[0] != 0xF0) return; // not char var findhandle = FindFirstFile(workstring, &finddata); if (findhandle == INVALID_HANDLE_VALUE) { stocvar("", output); return; } FindClose(findhandle); // for FILESIZE, output result as 12 digit string if (verb == V_SIZE) { itossize(finddata.nFileSizeLow, workstring, 12); stocvar(workstring, output); dbcsetover(0); return; } // for others, store the time in filetime and format it if (verb == V_CREATE) FileTimeToLocalFileTime(&finddata.ftCreationTime, &ft); else if (verb == V_WRITE) FileTimeToLocalFileTime(&finddata.ftLastWriteTime, &ft); else if (verb == V_READ) FileTimeToLocalFileTime(&finddata.ftLastAccessTime, &ft); else return; // should not happen FileTimeToSystemTime(&ft, &st); itossize(st.wYear, workstring, 4); itossize(st.wMonth, &workstring[4], 2); itossize(st.wDay, &workstring[6], 2); itossize(st.wHour, &workstring[8], 2); itossize(st.wMinute, &workstring[10], 2); itossize(st.wSecond, &workstring[12], 2); itossize(st.wMilliseconds / 10, &workstring[14], 2); for (i1 = 0; i1 < 16; i1++) { if (workstring[i1] == ' ') workstring[i1] = '0'; } stocvar(workstring, output); dbcsetover(0); return; } // convert string to char var static void stocvar(char *src, unsigned char *dest) { int i1, i2; i1 = strlen(src); if (dest[0] & 0x80) { // big char var if (!i1) dest[1] = dest[2] = dest[3] = dest[4] = 0; else { dest[1] = 1; dest[2] = 0; i2 = dest[5] + (dest[6] << 8); if (i1 > i2) i1 = i2; dest[3] = i1 & 0xFF; dest[4] = (i1 >> 8); memcpy(&dest[7], src, i1); } } else { // small char var if (!i1) dest[0] = dest[1] = 0; else { dest[0] = 1; i2 = dest[2]; if (i1 > i2) i1 = i2; dest[1] = i1; memcpy(&dest[3], src, i1); } } } // convert char var or literal to string static int cvarlittos(unsigned char *src, char *dest) { int fp, len; if (src[0] == 0xe0 || src[0] == 0x0e1) { // literal len = src[1]; memcpy(dest, &src[2], len); dest[len] = 0; return(0); } if (!src[0] & 0x80) { // small char var fp = src[0]; if (!fp) dest[0] = 0; else { len = src[1] - fp + 1; memcpy(dest, &src[fp + 2], len); dest[len] = 0; } return(0); } if (src[0] & 0xf0) { // big char var fp = src[1] + (src[2] << 8); if (!fp) dest[0] = 0; else { len = src[3] + (src[4] << 8) - fp + 1; memcpy(dest, &src[fp + 6], len); dest[len] = 0; } return(0); } return(1); } // convert integer to string static void itossize(int n, char *dest, int size) { dest[size] = 0; while (size) { dest[--size] = n % 10 + '0'; n /= 10; if (!n) break; } while (size) dest[--size] = ' '; } // END OF EXAMPLE C PROGRAM DB/C Class Schedule Class Date Location DB/C 9 Fundamentals June 1997 Oak Brook, IL DB/C 9 Advanced Features June 1997 Oak Brook, IL For information, contact Judi Tamkevic at: voice 630.572.0240 fax 630.572.0390 email dbc@swc.com