home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / dbase / tn9103.arj / TN0391.TXT
Text File  |  1991-07-16  |  65KB  |  1,673 lines

  1. 1  Read Me First
  2.  
  3. These articles are reprinted from the March '91 edition of TechNotes/dBASE
  4. IV.  Due to the limitations of this media, certain graphic elements such as
  5. screen shots, illustrations and some tables have been omitted.  Where
  6. possible, reference to such items has been deleted.  As a result,
  7. continuity may be compromised.  
  8.  
  9. TechNotes is a monthly publication from the Ashton-Tate Software Support
  10. Center.  For subscription information, call 800-545-9364.
  11.  
  12.  
  13.  
  14. 2  Q&A
  15.  
  16. Command Editing On the Fly
  17.  
  18. Behind every dot prompt expression there's an editing window.
  19.  
  20. There's No Place Like Ctrl-Home
  21.  
  22. My dot prompt expressions are starting to be quite lengthy.  As the amount
  23. of parenthetical insets increases, I find it more and more difficult to
  24. scroll back and forth trying to determine where possible errors exist. 
  25. What can I do to make debugging dot prompt expressions less cumbersome?
  26.  
  27. By pressing Ctrl-Home, you enter the text editor.  Here, you can use the
  28. Enter key to break complex expressions, thus becoming easier to view (it's
  29. all on the screen, either auto-wrapped or explicitly broken by lines) and
  30. debug.  To return to the dot prompt, simply press Ctrl-End and your command
  31. is processed.  To cancel, press Escape.
  32.  
  33. You most likely have never gotten to the point where your expressions
  34. exceed 254 characters.  In that case, you would receive the warning that
  35. your command was too long and how to work around the limitation.  
  36. Memo Exporting in WordPerfect
  37.  
  38. Although I've been able to export databases without memos into WordPerfect,
  39. I'm curious as to which command to use to also export memo information into
  40. that environment.  Is there a way to do this?
  41.  
  42. It is easy enough to export a file without memos into a more universal text
  43. format such as:
  44.  
  45. COPY TO <txtfile> DELIMITED
  46.  
  47. and then translate the file into a WordPerfect format using the CONVERT
  48. utility that WordPerfect provides.  However, when translating a file and
  49. its memo information, a small program is required, as shown below.  
  50.  
  51. SET TALK OFF
  52. USE <filename>
  53. SET ALTERNATE TO Txtout
  54. SET ALTERNATE ON
  55. SCAN
  56.   ? '"'
  57.   ?? TRIM(<charfield>)
  58.   ?? '",'
  59.   ?? LTRIM(STR(<numfield>, 10, 2))
  60.   ?? ',"'
  61.   ?? DTOS(<datefield>)
  62.   ?? ',"'
  63.   ?? IIF(<logicfld>, ".T.", ".F.")
  64.   ?? ',"'
  65.   lcnt = 1
  66.   DO WHILE lcnt < MEMLINE(<memofld>) + 1
  67.     ?? MLINE(note, lcnt)
  68.     lcnt = lcnt + 1
  69.   ENDDO
  70.   ?? '"'
  71. ENDSCAN
  72. CLOSE ALTERNATE
  73. RETURN
  74.  
  75.  
  76. The ?? lines where TRIM(), DTOS() and IIF() are used on various field types
  77. illustrate how to do such fields.  Additionally, the ?? statements which
  78. contain only commas, double and single quotes are important to delimit the
  79. different fields correctly.  The italic markers shown in angle brackets
  80. should be replaced with the actual names of the fields of your database. 
  81. They can appear in whatever order you prefer.
  82.  
  83. In the WP directory run CONVERT.  When prompted, enter the input filename
  84. (with .TXT extension) and the output filename.  Select option 9 from the
  85. next menu.  Enter a comma as a field delimiter, and as record delimiter,
  86. type {13}{11}.  The character to strip out would be a double quote.  After
  87. entering these parameters, process the conversion. 
  88. The Letter of the Law (of Indexing)
  89.  
  90. I am attempting to index on a field that contains part numbers.  The part
  91. numbers sometimes begin with letters, sometimes with numbers.  What I want
  92. is an index that shows the part numbers beginning with letters first
  93. (ascending alphabetically).  Afterwards, the part numbers starting with
  94. numbers would be listed sequentially.  None of the ASCII or dictionary
  95. sorts can accomplish this.  Is there a way?
  96.  
  97. Your request is possible by way of a complex index expression.  Suppose
  98. your part number field is 10 characters in width and is called PART.  Your
  99. index expression would appear as follows:
  100. INDEX ON STR(VAL(Part), 10) + SUBSTR(Part, 1, 10) TAG <tagname>
  101.  
  102. One exception to this index expression would prevent part numbers with
  103. leading zeros from being considered as a numeric (the VAL() function would
  104. remove the leading zeros in its string to numeric conversion).  So a part
  105. number such as 00AJ98207 would be evaluated starting from its third
  106. position, or the value "A" since the leading zeros would be stripped by the
  107. STR() and VAL() conversion.  If a part number started with a letter, it
  108. would be interpreted as a zero after being filtered by the VAL() function.
  109.  
  110. You might try displaying the above index expression from the dot prompt to
  111. see the effect of the expression on your field.  
  112.  
  113. ? STR(VAL(Part), 10) + SUBSTR(Part, 1, 10) 
  114.  
  115. This index expression would not be most ideal for a SEEK operation and is
  116. more suited toward a logical sort for reporting purposes.  However, seeing
  117. how the index expression is laid out will give you a good idea as to how
  118. you might SEEK with such an index.
  119. Save the Last Edit For Me
  120.  
  121. I have developed a format screen in which the last field must have an
  122. editing condition satisfied.  The validation works fine except for those
  123. times where I realize there is data in above fields that requires further
  124. editing.  In that case, I am stuck in the last field requiring the valid
  125. entry and cannot get back up to other fields.  Once I enter an invalid
  126. entry, I am forced to enter something valid but then the edit screen flips
  127. to the next record to be entered.  I tried using SET CONFIRM ON to no
  128. avail.  Is there any way around this, short of rearranging the fields?
  129.  
  130. I was just about to say you could rearrange the fields.  But, seriously, if
  131. that isn't a viable option and your format screen is aesthetically perfect
  132. in its present form, you might want to modify your valid clause in your
  133. screen design.  Under Edit options: Accept value when, add the following to
  134. your present condition:
  135.  
  136. .OR. LASTKEY() = 5
  137.  
  138. What this does is to allow the pressing of the Up-arrow key as part of the
  139. validity check.  That way, regardless of whether you've entered valid data
  140. in the last field, you can always go to previous fields.
  141.  
  142. There is one drawback to this method.  Once you've backed up to previous
  143. fields, leaving an invalid entry in the last field, you could easily skip
  144. to other records with the PgUp or PgDn key.  These, of course, can be
  145. restricted from use by the use of ON KEY LABEL statements earlier in your
  146. program.  
  147.  
  148. If the extra work required for this key trapping seems too much, you might
  149. want to take my first suggestion, rearrange the fields.  
  150.  
  151.  
  152.  
  153. 3  Converting Text Files to Postscript
  154.  
  155. Converting Text Files to PostScript
  156.  
  157. Martin Leon
  158.  
  159. Filtering output files through this utility allows them to print on a
  160. PostScript printer.
  161.  
  162. Although the dBASE IV version 1.1 implementation of PostScript printer
  163. support is adequate for most needs, there is one situation where you will
  164. not be able to get your output to print on a PostScript printer.  Many
  165. programmers often choose to create printed output by using SET DEVICE TO
  166. PRINT and @..SAY commands to produce the desired output.  Although there is
  167. nothing wrong with this technique, it would take a lot of extra coding to
  168. get the same program to be able to print to a PostScript printer.  The
  169. problem is twofold.   First, when you use the @..SAY command to output to a
  170. printer, the dBASE IV print engine is bypassed and has no influence over
  171. the resulting output.  Secondly, the PostScript printer is not expecting to
  172. receive lines of text to be printed, but rather is expecting a series of
  173. commands.  
  174.  
  175. With most printers, it doesn't matter if you bypass the dBASE IV print
  176. engine because as long as they receive a stream of ASCII characters (as
  177. with the @..SAY technique), they print the characters on the page as they
  178. were sent.  However, if you do this with a PostScript printer, the printer
  179. tries to interpret what you're sending to it as instructional data rather
  180. than literal output.  The end result is that the printer light blinks,
  181. indicating that the printer is processing your print job, but nothing is
  182. ever printed, leaving you tearing at your hair, banging on the top of the
  183. printer, rebooting your computer and a number of other human conditional
  184. responses to an uncooperative computer operation.
  185.  
  186. The C program PostOut.EXE presented here provides a means for you to get
  187. around this obstacle.  In order to understand how the program works we must
  188. first discuss how PostScript works and how it is that dBASE IV is able to
  189. print to a PostScript printer.
  190.  
  191. What Is PostScript?
  192.  
  193. PostScript is a very powerful page description language, hence its
  194. acceptance as a standard in the Desktop Publishing realm.  It's a
  195. programming language that allows you to define in detail everything you
  196. want printed on a page.  With it you can draw lines, boxes, circles, and
  197. other objects, fill them in with black, white or grayscale.  It is also
  198. possible to define font attributes, print text, change the orientation of
  199. how things are printed on the page, and a host of other maneuverings. 
  200. PostScript has changed the course of modern publishing, personalizing it in
  201. much the same way as the first PCs made home computing a possibility.
  202.  
  203. PostScript comes complete with its own commands, operators, and syntax. 
  204. Because of its extreme flexibility in laying out a page, it takes a pretty
  205. large program to print to a PostScript printer.  Compared to what is
  206. required to print to a dot matrix it is a much more complex exchange of
  207. data.  But consider the difference in quality.  
  208.  
  209. Fortunately for us, someone already put a lot of time and thought into
  210. designing such a program,  making it not only possible but fairly
  211. transparent to the user for dBASE IV to print to PostScript printers.  This
  212. program is provided in a file called the PostScript download file
  213. (PostScri.DLD).  It defines several procedures (called macros) and
  214. maintains several memory variables to let the printer know which font to
  215. use, where on the page to put the next line of text, when a page is full,
  216. how to eject a page, and so on.  None of these operations are handled in
  217. the same manner as they would be with conventional printers.
  218.  
  219. To begin with, the ASCII control characters such as the form feed command
  220. (ASCII 12), carriage return (ASCII 13) and line feed (ASCII 10) are not
  221. interpreted by the PostScript printer to have the same effect as they do on
  222. other types of printers.  In order to emulate the functionality of other
  223. printers, the PostScript driver PostScri.PR3 translates these characters
  224. into the names of macros that are defined in the download file.  
  225.  
  226. A form feed command, which is what tells most printers to advance the paper
  227. to the beginning of the next page, is translated into the name of a macro
  228. called "FF".  This macro tells the PostScript printer to print everything
  229. that has been defined in printer memory up to this point, eject the page,
  230. and re-initialize several memory variables to be able to start defining the
  231. text for the next page to be printed.  
  232.  
  233. The combination of carriage return and line feed is translated into a macro
  234. called "CRLF".  This macro tells the printer what is to be printed on the
  235. current line and to reset some more memory variables so that the printer
  236. knows where to start the next line of text.  
  237.  
  238. In PostScript, the command to put a line of text on the page is "show". 
  239. The text you want printed should be surrounded by parentheses before the
  240. command.  The macros and memory variables in the download file dictates at
  241. what coordinates to print everything.  The DLD file also has macros for
  242. turning compressed print on and turning landscape printing on.
  243.  
  244. Here's a small example of what it would take to print two lines using the
  245. download macros:
  246.  
  247. Init                  <- initializes printer settings and memory
  248. variables
  249. Norm                  <- do not use any font attributes like bold
  250. or italic
  251. (ECHO OFF)show          <- put the words ECHO OFF at current
  252. row/column coordinates
  253. CRLF                  <- change row/column coordinates to be able
  254. to print on next line
  255. (PROMPT $P$G)show 
  256. CRLF
  257. FF                        <- print out everything that has
  258. been defined for current page and eject it
  259.                         and prepare the next page for
  260. printing
  261.  
  262. Of course, this all depends on the download file being sent to the printer
  263. immediately before sending these commands.  Without the download macros, it
  264. would take many, many more commands to accomplish the simplest output.
  265.  
  266. About the Utility
  267.  
  268. The C routine starting on page 10 uses this basic knowledge of PostScript
  269. and the dBASE IV PostScript download file to translate a standard ASCII
  270. file into a PostScript document.  It contains switches to allow you to
  271. print using compressed print and/or landscape printing.  You can set tab
  272. character spacing, page length, and choose one of three fonts.  You can
  273. direct the PostScript output to any output device (LPT1, COM1, CON, PRN,
  274. and so on) or to another file.  Now you can continue using @..SAY
  275. conventions, but, instead, you would redirect the printer output to a
  276. file.  Once the file is created, this program will allow you to translate
  277. the file and send it to the desired destination along with the POSTSCRI.DLD
  278. file.
  279.  
  280. After retrieving and verifying the command line arguments, POSTOUT simply
  281. looks for carriage return/line feed combinations, form feed characters and
  282. tab characters.  Each line of text is prefaced with a "(" and terminated
  283. with ")show", enabling that line to be printed.  Carriage return/line feeds
  284. are replaced with the CRLF macro, form feeds are replaced with the FF
  285. macro, and tab characters are replaced with the appropriate number of
  286. spaces.  The program counts how many lines have been put on a page and
  287. issues a form feed macro when the specified maximum has been reached.  Low
  288. order and high order ASCII characters are replaced by an asterisk since
  289. PostScript has a different symbol set for these characters.  This means you
  290. will not be able to use box and line drawing.
  291.  
  292. You can use PostOut for any ASCII text file.  Suppose you're on a network
  293. and you want to print your AutoExec.BAT file to a PostScript printer
  294. assigned to LPT2 and print in compressed print Just have PostOut.EXE in
  295. your path as well as having the PostScri.DLD file in the same directory as
  296. POSTOUT, then issue the following command from the DOS level:
  297.  
  298. POSTOUT AUTOEXEC.BAT LPT2 /C
  299.  
  300. It's recommended you use the default font (Courier) because it is a fixed
  301. space font, like the one you see on the screen when you are in dBASE IV. 
  302. If you use any of the other fonts, the text will not line up like it would
  303. on the screen.  The other fonts would be used for readability or preferred
  304. appearance, but since they are proportional fonts (each letter has a
  305. different width), it would be difficult to get a document to line up the
  306. way you want it when using them.
  307.  
  308. The program was created using MicroSoft Quick C and is not hardware
  309. dependent.  You should be able to compile it using any C compiler and have
  310. it run on any machine.  It does use the ANSI C standard function
  311. prototyping for the two functions that get command line arguments.  If your
  312. C compiler does not conform to the ANSI standard, you will need to change
  313. the function headers for these two functions and change their function
  314. declarations.  
  315.  
  316. One last note: If you have trouble printing to your PostScript printer,
  317. whether it be from dBASE IV or using POSTOUT, try re-extracting the
  318. download (.DLD) file from the file DRIVERS.EXE.  Sometimes people will use
  319. a text editor that puts an EOF character into the DLD file and this
  320. prevents it from working properly.  To re-extract the file, go to your
  321. dBASE IV sub-directory and type in the command:
  322.  
  323. DRIVERS -O POSTSCRI.DLD 
  324.  
  325. This will cause a fresh copy of the download file to be extracted,
  326. overwriting your existing one. 
  327.  
  328. PostOut.C
  329.  
  330. /* POSTOUT ─ Uses dBase IV .DLD file to enable you to output a standard
  331.    ASCII text file to a PostScript printer without modifying it.  Prints
  332.    7 bit ASCII set.  Higher ASCII characters are converted to *'s.  
  333.    Tabs are converted to spaces as specified by command line parameters.
  334.  
  335.    Created by Martin Leon, Ashton Tate Software Support
  336.    Version 1.0a */
  337.  
  338. #include <stdio.h>
  339. #include <string.h>
  340. #include <stdlib.h>
  341. #include <ctype.h>
  342. #define MAXPATH 256
  343.  
  344. FILE *infile;
  345. FILE *outfile;
  346. FILE *dldfile;
  347.  
  348. /* ANSI C function prototypes */
  349. int get slash arg( char switch letter, char *argv[] );
  350. char *get eq arg( char prefix, char *argv[] );
  351.  
  352. /* Names of PostScript macros created in the DLD file */
  353. char crlf[] = "CRLF\n";
  354. char show[] = "show\n";
  355. char ffeed[] = "FF \n";
  356. char font[3][7] = {"1FONT\n","2FONT\n","3FONT\n"};
  357.  
  358. /* Other variables */
  359. char tab[11] = "          "; /* tab set to max of 10 spaces */
  360. char homedir[MAXPATH];
  361. char temp[MAXPATH];
  362. char *ptr;
  363. int linecount = 0;
  364. int max lines = 0;
  365. int c = '\0';
  366. int x = 0;
  367. int tabspace;
  368. int fontnumber = 0;
  369. int in parens = 0;
  370.  
  371. int main( int argc, char *argv[] )
  372. {
  373.     /* Check for at least 2 command line arguments */
  374.     if( argc < 3 ) {
  375.         printf( "\nPOSTOUT.EXE v1.0a\n\n"\
  376.             "Input and output file name required.  E.G.:
  377. POSTOUT IN.TXT OUT.TXT\n"\
  378.             "Output file can be any output device or file
  379. name.\n"\
  380.             "Optional switches (can be used in any
  381. order/combination):\n"\
  382.             "    /C for compressed print\n"\
  383.             "    /L for landscape printing\n"\
  384.             "    T=<value between 3 and 10> for tab stop
  385. spacing <default is 3>\n"\
  386.             "    F=<value between 1 and 3> for font
  387. number\n"\
  388.             "        1 is Courier, fixed spacing
  389. <default font>\n"\
  390.             "        2 is Helvetica, proportional
  391. spacing\n"\
  392.             "        3 is Times, proportional
  393. spacing\n"\
  394.             "    L=<value between 45 and 81> for page length
  395. <default is 63>\n");
  396.         return 1;
  397.     }
  398.  
  399.     /* Check for tab setting switch and adjust tab spacing if needed */
  400.     tabspace = atoi( get eq arg( 'T', argv ) );
  401.     if( tabspace < 3 || tabspace > 10 ) {
  402.         if( tabspace ) {
  403.             printf( "\nTab setting must between 3 and 10\n" );
  404.             return 1;
  405.         }
  406.     }
  407.     if( tabspace ) {
  408.         ptr = tab;
  409.         ptr += strlen( tab );
  410.         while( (int)strlen( tab ) > tabspace ) {
  411.             ptr─;
  412.             *ptr = '\0';
  413.         }
  414.     }
  415.     else
  416.         strcpy( tab ,"    " );
  417.  
  418.     /* Check for font setting switch and set fontnumber */
  419.     fontnumber = atoi( get eq arg( 'F', argv ) );
  420.     if( fontnumber > 3 ) {
  421.         printf( "\nFont number must be between 1 and 3\n" );
  422.         return 1;
  423.     }
  424.  
  425.     /* Check for page length setting and adjust if needed */
  426.     max lines = atoi( get eq arg( 'L', argv ) );
  427.     if( max lines < 45 || max lines > 81 ) {
  428.         if( !max lines )
  429.             if( get slash arg( 'L', argv ) )
  430.                 max lines = 45;
  431.             else
  432.                 max lines = 63;
  433.         else {
  434.             printf( "\nPage length must be between 45 and 63\n"
  435. );
  436.             return 1;
  437.         }
  438.     }
  439.  
  440.     /* Make sure input file can be opened */
  441.     if( ( infile = fopen( argv[1], "rt" ) ) == NULL ) {
  442.         printf( "\nCannot open %s for input.\n", argv[1] );
  443.         fcloseall();
  444.         return 1;
  445.     }
  446.     /* Make sure output file can be opened */
  447.     if( ( outfile = fopen( argv[2], "wt" ) ) == NULL ) {
  448.         printf( "\nCannot open %s for output.\n", argv[2] );
  449.         fcloseall();
  450.         return 1;
  451.     }
  452.     /* Determine directory POSTOUT was called from and add DLD file
  453. name to it*/
  454.     /* This method will only work with DOS version 3.0 or higher */
  455.     strcpy( homedir, argv[0] );
  456.     ptr = homedir;
  457.     ptr += (strlen( homedir ) - 1 );
  458.     while( *ptr != '\\' )
  459.         ptr─;
  460.     strcpy( ++ptr, "POSTSCRI.DLD" );
  461.  
  462.     /* Make sure dld file can be openned from same dir as POSTOUT.EXE
  463. */
  464.     if( ( dldfile = fopen( homedir, "rt" ) ) == NULL ) {
  465.         printf( "\nCannot open %s\n"\
  466.             "DLD file must be in same directory as
  467. POSTOUT.EXE\n", homedir );
  468.         fcloseall();
  469.         return 1;
  470.     }
  471.  
  472.     /* copy dld file to output */
  473.     printf( "\nWriting Output\r" );
  474.     while( (c = fgetc( dldfile )) != EOF )
  475.         fputc( c, outfile );
  476.  
  477.     /* If you are not using an HP III with a PostScript cartridge,
  478.         add comment delimiters to next line */
  479.     fputs( "/tlxoff 18 def \n", outfile );
  480.  
  481.     /* Insert call in output to DLD Init macro to initialize printjob
  482. */
  483.     fputs( "Init\n", outfile );
  484.  
  485.     /* If /L command line argument was used, set to landscape printing
  486. */
  487.     if( get slash arg( 'L', argv ) )
  488.         fputs( "LAND\n", outfile );
  489.  
  490.     /* If /C command line argument was used, set to compressed printing
  491.         otherwise set to normal printing */
  492.     if( get slash arg( 'C', argv ) )
  493.         fputs( "Cmp+\n", outfile );
  494.     else
  495.         fputs( "Norm\n", outfile );
  496.  
  497.     /* Send font selected or set to default of first font */
  498.     if( fontnumber != 0 )
  499.         fputs( font[ fontnumber - 1 ], outfile );
  500.     else
  501.         fputs( font[0], outfile );
  502.     /* Format text for PostScript and send to output */
  503.     fputc( '(', outfile );
  504.     in parens = 1;
  505.     while( ( c = fgetc( infile ) ) != EOF ) {
  506.         /* Translate form feed characters */
  507.         if( c == '\x0C' ) {
  508.             if( in parens ) {
  509.                 fputc( ')', outfile );
  510.                 in parens = 0;
  511.                 fputs( show, outfile );
  512.             }
  513.             fputs( ffeed, outfile );
  514.             linecount = 0;
  515.             fputc( '(', outfile );
  516.             in parens = 1;
  517.             continue;
  518.         }
  519.         /* Replace carraige returns with CRLF macro in DLD */
  520.         if( c == '\n' ) {
  521.             /* If at last line on page, call FF macro in DLD */
  522.             if( ++linecount == max lines ) {
  523.                 if( in parens ) {
  524.                     fputc( ')', outfile );
  525.                     in parens = 0;
  526.                 }
  527.                 fputs( show, outfile );
  528.                 fputs( ffeed, outfile );
  529.                 fputc( '(', outfile );
  530.                 in parens = 1;
  531.                 linecount = 0;
  532.                 continue;
  533.             }
  534.             /* Close parens on SHOW macro in DLD and add CRLF
  535. macro */
  536.             if( in parens ) {
  537.                 fputc( ')', outfile );
  538.                 in parens = 0;
  539.             }
  540.             fputs( show, outfile );
  541.             fputs( crlf, outfile );
  542.             fputc( '(', outfile );
  543.             in parens = 1;
  544.             continue;
  545.         }
  546.         /* Replace some control character and all ASCII characters
  547.             with values greater than 128 in input with an
  548. asterisk */
  549.         if( c > '\x7F' || c < '\t' || (c < ' ' && c > '\n') ) {
  550.             if( c == '\t' )
  551.                 fputs( tab, outfile );
  552.             else
  553.                 fputc( '*', outfile );
  554.             continue;
  555.         }
  556.         /* Replace tab character in input with number of spaces
  557. determined by
  558.             command line argument */
  559.         if( c == '\t' ) {
  560.             fputs( tab, outfile );
  561.             continue;
  562.         }
  563.  
  564.         /* Replace backslashes and parens in input to \\ and \( and
  565. \) */
  566.         if( c == '\\'  || c == '(' || c == ')' )
  567.             fputc( '\\', outfile );
  568.  
  569.         fputc( c, outfile );
  570.     }
  571.  
  572.     /* End of file reached ─ close parens for SHOW macro */
  573.     fputc( ')', outfile );
  574.     fputs( show, outfile );
  575.     /* Send FF for macro in DLD */
  576.     fputs( ffeed, outfile );
  577.     /* Send EOT character to indicate end of job */
  578.     fputc( '\x04', outfile );
  579.     /* Close files */
  580.     fcloseall();
  581.     printf( "File Written to %s\n", argv[2] );
  582.     return 0;
  583. }
  584.  
  585.  
  586. /* Scans all command line arguments for one that begins with a specific
  587.     letter and is followed by an = sign.  Returns pointer to first
  588. character
  589.     after the equal sign */
  590.  
  591. char *get eq arg( char prefix, char *argv[] ) /* ANSI C function header */
  592. {
  593.     char *temptr = *argv;
  594.     while( *argv )
  595.     {
  596.         char *ptr = strcpy( temp, *argv );
  597.         temptr = ptr;
  598.  
  599.         if( toupper( *ptr++ ) == prefix && *ptr++ == '=' )
  600.             return ptr;
  601.         argv++;
  602.     }
  603.     temptr = (char *)argv;
  604.     return temptr;
  605. }
  606.  
  607. /* Scans all command line arguments for one that begins with a slash and is
  608.     followed by a specific letter.  Returns 1 if switch is found in
  609. arguments */
  610.  
  611. int get slash arg( char switch letter, char *argv[] ) /*ANSI C function
  612. header*/
  613. {
  614.     while( *argv )
  615.     {
  616.         char *ptr = strcpy( temp, *argv );
  617.  
  618.         if( *ptr == '/' &&  toupper( *(++ptr) ) == switch letter )
  619.             return 1;
  620.         argv++;
  621.     }
  622.     return 0;
  623. }
  624. /* EOF: PostOut.C */
  625.  
  626.  
  627.  
  628.  
  629.  
  630.  
  631. 4  Popup Combinations to Go
  632.  
  633. Popup Combinations To Go
  634.  
  635. Adam Menkes
  636.  
  637. Simple to seemingly impossible popups.
  638.  
  639. Many people would like the ability to have multiple field popups and
  640. conditional popups in their applications.  There are several ways to do
  641. this using pseudo-popups such as a BROWSE NOEDIT NOAPPEND NODELETE or using
  642. arrays and painting the screen.  This article will progress from showing
  643. you how to do a simple popup a few different ways to doing multi-field
  644. popups with fields from several databases and have a seemingly impossible
  645. conditional multiple multi-field popup active.  You will also learn how to
  646. achieve an @.GET inside a POPUP (for both adding and editing), and various
  647. other tips, tricks, and techniques that show that there is more than one
  648. way to SCAN a CAT.  Hey, how about that?  I could always get a job as a
  649. comedian if this programming thing doesn't pan out.
  650.  
  651. The basic concept of defining a popup lies in how to define the selections
  652. within the popup.  This is not as obvious as it seems. You can 
  653.  
  654. DEFINE POPUP <Popup Name> PROMPT FIELD <Field Name> 
  655.  
  656. but not 
  657.  
  658. DEFINE POPUP <Popup Name> PROMPT FIELDS <Field1>, <Field2>, .<Fieldn>.  
  659.  
  660. But what if you could store Fields 1 to n to a character variable, and
  661. define that as the BAR of the POPUP?   To further explore this concept,
  662. consider the database structures below:
  663.  
  664. Given these database structures, and assuming the PHONE1 database is
  665. active, the following is the definition for a simple POPUP of last names :
  666.  
  667. DEFINE POPUP SimplePop FROM 1, 40 PROMPT FIELD Lname
  668.  
  669. Alternatively, the same POPUP can be defined this way :
  670.  
  671. DEFINE POPUP ScanPop FROM 1, 40
  672. X = 1
  673. SCAN
  674.   DEFINE BAR X of ScanPop PROMPT Lname
  675.   X = X + 1
  676. ENDSCAN
  677.  
  678. Now, suppose you wanted a conditional POPUP that displayed a listing of
  679. either the last name or, there was no last name, the name of the company. 
  680. All you need do is change the DEFINE BAR in the above SCAN/ENDSCAN to read:
  681.  
  682. DEFINE BAR X of ScanPop PROMPT ;
  683. IIF(LEN(TRIM(CoName)) = 0, Lname, CoName)
  684.  
  685. For multiple field popups :
  686.  
  687. DEFINE BAR X of ScanPop PROMPT ;
  688. TRIM(Fname) + " " + TRIM(Lname) + " " + DTOC(DateIn)
  689.  
  690. Suppose Phone1.DBF is open in work area 1 and Phone2.DBF is open in work
  691. area 2 (SET RELATION TO Key INTO B) and you want a multi-field popup using
  692. fields from both databases :
  693.  
  694. DEFINE BAR X of ScanPop PROMPT ;
  695. TRIM(Fname) + " " + TRIM(Lname) + " " + B->Phone
  696.  
  697. This will only select the first match for the Phone number in area 2 for
  698. each KEY in area 1.  Later, you will see how to get them all. 
  699.  
  700. Assume you wanted a POPUP with "header" and "footer" information.  The
  701. information can be as many lines and contain any text and fields you wish :
  702.  
  703. DEFINE BAR 1 of ScanPop PROMPT "Any Header Info You Wish" SKIP
  704. DEFINE BAR 2 of ScanPop PROMPT "Line 2 Info " + AnyFields SKIP
  705.  
  706. x = 3     && New starting BAR number.
  707. SCAN ...
  708.   DEFINE BAR X OF ...
  709. x = x + 1
  710. ...
  711. ENDSCAN
  712.  
  713. DEFINE BAR X of ScanPop PROMPT "Any Footer Info You Wish" SKIP
  714. DEFINE BAR X + 1 of ScanPop PROMPT "Line 2  " + AnyFields SKIP
  715. ...
  716.  
  717. The SKIP is optional and is used in this instance so that the header and
  718. footer information is not selectable.  AnyFields can be any field or
  719. combination of fields from one or more databases in any work area.
  720.  
  721. Another method of defining a pick list is to DEFINE a MENU, and DEFINE PADs
  722. within that menu.  Normally, a MENU is thought of as a horizontal bar
  723. menu.  Since positioning is determined by the programmer, you can DEFINE
  724. PADs within the MENU vertically (or randomly if so desired). 
  725.  
  726. Using the same principals discussed earlier with ScanPop, below is the code
  727. for a simple picklist using a MENU instead of a POPUP.  The result looks
  728. identical to a popup but functions somewhat differently:
  729.   
  730. DEFINE MENU ScanMenu
  731. x = "1"   && Note the quotes (").
  732. SCAN
  733.   DEFINE PAD Pad&x of ScanMenu PROMPT Lname AT VAL(x), 40
  734. *- Note: The 40 can be any column position.
  735.     x = LTRIM(STR(VAL(x) + 1))
  736. *- Increment a number (x) stored as a character variable.
  737. ENDSCAN
  738.  
  739. How is this any different from defining a POPUP?  Well, a menu can not be
  740. defined beyond the screen length, so you are limited to a picklist of 25
  741. items (43 when your display is set to EGA43 mode), or are you?  As the
  742. DEFINE PAD also optionally uses the AT parameter to specify a column
  743. position, and the row is defined as a variable (VAL(x)), the column can
  744. also be defined as a variable, which in effect, gives you a multi-column
  745. picklist, limited by the width of the PADs times the number of PADs
  746. vertically times the number of PADs horizontally and also depends upon
  747. available memory.  You could also define the PADs AT MOD(VAL(x), 24) or
  748. MOD(VAL(x), 42) in 43 line mode (1 less than the screen length as the
  749. message line will blank the item) so that the picklist remains in one
  750. column.
  751.   
  752. DEFINE MENU ScanMenu
  753. x = "1"            && Note that x is character type.
  754. y = 1                && Added for muli-column display.
  755. SCAN
  756.   DEFINE PAD Pad&x of ScanMenu PROMPT Lname ;
  757.   AT MOD(VAL(x), 24), y
  758.   x = LTRIM(STR(VAL(x) + 1))
  759.   *- Increment a number (x) stored as a character variable.
  760.  
  761.   IF MOD(VAL(x), 24) = 0
  762.     y = y + 17
  763.     * Increment the column position by 17 (or whatever number
  764.     * of column positions to display the next portion of the
  765.     * picklist.)
  766.   ENDIF
  767. ENDSCAN
  768.  
  769. Since row and column positions are defined as variables, you can see how
  770. you could easily define a menu where the items were defined to be visually
  771. more interesting, such as defining the PADs at every other row, or defining
  772. them in a step fashion.  Using the above program code, simply change:
  773.  
  774. ...  AT MOD(VAL(x), 24 / 2) * 2, y    && Every other row.
  775.  
  776. or more generally :
  777.  
  778. ...  AT MOD(VAL(x), 24 / n) * n, y    && Every nth row.
  779.  
  780. To step, incrementing one column position per defined bar:
  781.  
  782. ... AT MOD(VAL(x), 24), y + VAL(x)
  783. or
  784. ... AT MOD(VAL(x), 24), y + MOD(VAL(x), 24)
  785.  
  786. IF y >= variable column position
  787.   y = y + number of spaces desired
  788. ENDIF
  789. *- Increments by 1 column position and y spaces between records 1-25 and
  790. records 26-50 etc.
  791.  
  792. Now, using this principal of variable row and column coordinates, you can
  793. see how the following menus are defined.  You can do "V", "X", "<", ">",
  794. "\", "/" rather easily and, in fact, could do any pattern or randomly if so
  795. desired. 
  796.  
  797. To get one or more ">" patterns, you need to reverse the direction of the
  798. step at the midpoint; line 12 in 25 line mode (lines 0 - 24), and line 21
  799. in 43 line mode (0-42).  PAD1 is at row 0 (the PAD number is always 1
  800. higher than the row) and the easiest way to have symmetry about a midpoint
  801. (line 12 in this example) is to start with the value of the midpoint (12)
  802. and subtract (or add) the appropriate number of rows.  Using the absolute
  803. value of the difference between the current row and the midpoint will
  804. provide this symmetry as 11 - 11 = 0, 11 - 10 = 1, and 11 - 12 = - 1
  805. [ABS(-1) = 1].  Here is the syntax for the ">" pattern:
  806.  
  807. DEFINE PAD pad&PadNum OF MenuPad AT MOD(VAL(PadNum), 24), ;
  808.   mCol + abs(12 + (1 - (INT(VAL(PadNum) / 24))  ;
  809.   - ABS(11 - MOD(VAL(PadNum), 24)))) PROMPT ; 
  810.   " (" + AC + ") " + Phone + " "
  811.  
  812. To reverse the direction:
  813.  
  814. DEFINE PAD pad&PadNum OF MenuPad AT MOD(VAL(PadNum), 24), ;
  815. mCol + ABS(12 + (1 - (INT(VAL(PadNum) / 24))  ;
  816. + ABS(11 - MOD(VAL(PadNum), 24)))) PROMPT ; 
  817. " (" + AC + ") " + Phone + " "
  818.  
  819. Can you spot the difference in the code?  There is only one : Line 3 has a
  820. minus sign for the "<" pattern and a plus sign for the ">" pattern.  Surely
  821. there must be an easier and better way to determine direction without
  822. duplicating code.  The wonders of macro substitution allow not only
  823. character substitution but commands and operators as well. 
  824.  
  825. Here's a way of performing the pattern in either direction:
  826.  
  827.   DEFINE PAD pad&PadNum OF MenuPad AT MOD(VAL(PadNum), 24), ;
  828.   mCol + ABS(12 + (1 - (INT(VAL(PadNum) / 24))  ;
  829.   &PlusMinus. ABS(11 - MOD(VAL(PadNum), 24)))) PROMPT ; 
  830.   " (" + AC + ") " + Phone + " "
  831.   * - PlusMinus is a character variable equal to "+" or "-"
  832.  
  833. Now, the "><" pattern is easy.  In the SCAN..ENDSCAN loop, add :
  834.  
  835. PlusMinus = IIF(mCol < 17, "-", "+")
  836. * 17 is the length of the PROMPT in this example.
  837.  
  838. These are just a few examples of the limitless possibilities of defining
  839. POPUPs and MENUs using SCAN..ENDSCAN with proper variable substitution of
  840. the row and column positions.
  841.  
  842. Now that you have these concepts wired (hopefully without too much
  843. short-circuiting), you can probably figure out how to have two "popups"
  844. active, regardless of whether or not they are multi-field or
  845. multi-relational popups.  Since you know from the documentation that two
  846. popups can not be active at the same time, but a MENU and a POPUP can, and
  847. since either one can be defined using SCAN / ENDSCAN with any fields from
  848. any related databases FOR any condition (or indexed conditionally), all you
  849. need to do is activate whichever popup coincides with each PAD.  Rather
  850. than determining this programmatically, you initialize each POPUP as you
  851. are defining your MENU PADs.  Each one of these popups will be activated by
  852. cursor movement keys (left-arrow, right-arrow, up-arrow, down-arrow, Home,
  853. End) if ON PAD is used, and if ON SELECTION PAD is used, the Enter key.
  854.  
  855. Below is the code for having both popups (actually one menu that looks like
  856. a popup and many popups of which only one is active at a time).  As you can
  857. see, there are two SCAN / ENDSCAN loops, one inside the other, which define
  858. your selections.  
  859.  
  860. USE Phone1 IN 1 ORDER Lname
  861. USE Phone2 IN 2 ORDER Phone
  862. DEFINE MENU MenuTest
  863.  
  864. SELECT 1
  865.  
  866. x= "1"
  867. SCAN
  868.   mKey = Key    && Related field in both databases.
  869.   DEFINE PAD Pad&x of MenuTest PROMPT ;
  870.   Lname + ", "+ Fname AT VAL(x), 10
  871.  
  872.     SELECT 2
  873.     DEFINE POPUP Pop&x FROM VAL(X), 52
  874.     y = 0   && No records may be selected.
  875.     SCAN for Key = mKey
  876.     *- If a matching record is FOUND(), increment the BARs,
  877.     *- DEFINE them, and initialize the ON SELECTION.
  878.       y = y + 1    
  879.       DEFINE BAR Y OF Pop&x PROMPT "(" + AC + ")"+Phone
  880.       ON SELECTION POPUP Pop&x DEACTIVATE MENU
  881.     ENDSCAN
  882.  
  883.     IF y > 0
  884.     *- If records ARE selected - 
  885.       ON PAD Pad&x OF MenuTest ACTIVATE POPUP Pop&x
  886.     *- ACTIVATE the appropriate POPUP.
  887.     ENDIF
  888.  
  889.     x = LTRIM(STR(VAL(x) + 1))
  890.     *- Increment a character numeric variable ("1" not 1).
  891.     SELECT 1
  892. ENDSCAN
  893. ACTIVATE MENU MenuTest
  894.  
  895. Again, keep in mind that the more pads, bars and popups defined, the more
  896. potential for "Insufficient memory" error messages. 
  897.  
  898. Other things you can do with POPUPs include having memo fields in the POPUP
  899. and data input or edited directly (or seemingly so) in the POPUP. These
  900. capabilities are actually quite simple.  In order to GET a variable inside
  901. a POPUP, you first DEFINE the POPUP as you normally would, then use ON
  902. SELECTION POPUP to DEACTIVATE the POPUP, immediately afterwards SHOW the
  903. POPUP, and GET the variable at a variable row and column position.  A
  904. partial example is listed below :
  905.  
  906. SEEK mKey   
  907. *─ mKey should already be initialized - i.e. mKey = "000001"
  908.  
  909. DEFINE POPUP PopPh FROM 14, 4
  910. DEFINE BAR 1 OF PopPh PROMPT SPACE(20) + "<ADD A NEW ENTRY>"
  911. DEFINE BAR 2 OF PopPh PROMPT REPLICATE(CHR(205), 70) SKIP
  912. x = 3
  913. SCAN WHILE KEY = mKey
  914.   DEFINE BAR x OF PopPh PROMPT " (" + AC + ") " + Phone +  ;
  915.   " x " + Ext + " " + PhDesc + " " + TimeSt + " " + TimEnd
  916.   x = x + 1
  917. ENDSCAN
  918.  
  919. ON SELECTION POPUP PopPh DEACTIVATE POPUP
  920. ACTIVATE POPUP PopPh
  921. MROW = ROW()
  922.  
  923. IF BAR() <> 1
  924.    mEdRec = BAR() - 2
  925. ELSE
  926.    mEdRec = 1
  927. ENDIF
  928.  
  929. SHOW POPUP PopPh
  930.  
  931. *─ This is where you would insert code to determine what 
  932. *─ record was selected and either store blanks (zeros) or 
  933. *─ the contents of the selected record to the memory vars.
  934.  
  935. IF mEdRec = 1
  936.   * - Set memory variables blank
  937. ELSE
  938.   * - Set memory variables equal to the value of respective fields
  939. ENDIF
  940.  
  941. SEEK mKey         
  942. *─ Reposition the record pointer to the first matching Key.
  943.  
  944. IF FOUND()
  945.   SKIP mEdRec - 1 
  946.   *─ Skip the appropriate number of records.
  947. ENDIF
  948.  
  949. IF LASTKEY() <> 27        && Esc key was not pressed.
  950.   @ mRow,  6 SAY "("
  951.   @ mRow,  7 GET mAC PICTURE "999"
  952.   @ mRow, 10 SAY ")"
  953.   @ mRow, 12 GET mPhone PICTURE "999-9999"
  954.   @ mRow, 20 SAY " x "
  955.   @ mRow, 23 GET mExt PICTURE "99999"
  956.   @ mRow, 29 GET mPhDesc PICTURE "XXXXXXXXXXXXXXXXXXXXXXXXX" 
  957.   @ mRow, 55 GET mTimeSt PICTURE "99:99!M"
  958.   @ mRow, 64 GET mTimEnd PICTURE "99:99!M"
  959.   READ
  960. ENDIF
  961.  
  962.  
  963. 5  Etc.
  964.  
  965. Etc.
  966. Performance Considerations
  967.  
  968. The question of dBASE IV performance has run the gamut from lost clusters
  969. and available memory to solar flares and a shift in the space time
  970. continuum. 
  971.  But just how do you explain an operation that runs slower now than it did
  972. in 1.0? 
  973.  
  974. The Need For Speed
  975.  
  976. Some users have informed us of speed decrease during REPLACE operations
  977. with dBASE IV version 1.1.  The same operation in dBASE IV version 1.0 was
  978. faster.  This may be due to delays caused by overlay swapping.
  979.  
  980. This can be the case when REPLACEing or APPENDing to indexed databases. 
  981. True, both versions had .MDX files but indexes are not the cause of the
  982. problem.  In version 1.1, the INDEX and REPLACE operations are in different
  983. segments of the very large DBASE.OVL file, thus causing a good deal of
  984. swapping in and out of memory.  This is what causes the slow down.  Here
  985. are some tips for speeding up performance in the new 1.1 environment:
  986.  
  987. ∙ Set up a RAM disk and SET DBTMP (a DOS environmental variable) equal to
  988. this RAM disk directory.
  989.  
  990. ∙ Free up as much DOS conventional memory as possible, getting rid of
  991. things such as ANSI.SYS (which most people don't need) in your CONFIG.SYS
  992. file.   Remove other non-essential drivers from your CONFIG.SYS as well. 
  993. If you're using a 4.x version of DOS, remove the DOS APPEND command from
  994. your AUTOEXEC.BAT file.  
  995.  
  996. ∙ For Novell users, get the Novell EMSNET3 and XMSNET3.  These will free up
  997. more DOS conventional memory by loading NET3 in extended or expanded
  998. memory.  The NetWire forum on CompuServe should have these utilities
  999. available.  You may also call Novell at 1-800-LANSWER.
  1000.  
  1001. ∙ Play "musical indexes".  Create an .MDX file called Empty with a tag of
  1002. the same name.  To do this, put any database into use and type in the
  1003. command:
  1004.  
  1005. INDEX ON " " FOR .F. TAG Empty OF Empty
  1006.  
  1007.   Prior to doing a large REPLACE, close the .DBF and rename its production
  1008. .MDX to another name.  Rename Empty.MDX to make it your temporary
  1009. production .MDX.  Use the database and perform your REPLACE or APPEND
  1010. operations.  Close the database, rename the two .MDX files back to their
  1011. original names and REINDEX.  Here is a series of commands that outline the
  1012. operation, using a file called Trans.DBF, which is assumed to be open:
  1013.  
  1014. USE && closes file
  1015. RENAME Trans.MDX TO Hold.MDX
  1016. RENAME Empty.MDX TO Trans.MDX
  1017. USE Trans
  1018. REPLACE ...
  1019.  
  1020. If all of the REPLACEs are to be done on a single field, the process is
  1021. much simpler.  A tag for a single field can be updated by the following
  1022. command:
  1023.  
  1024. SET ORDER TO <tagname>
  1025. REPLACE NEXT 1 <fieldname> WITH <fieldname>
  1026.  
  1027. This last suggestion adds some code to your program, but it will work
  1028. around the slowness you're experiencing.  
  1029.  
  1030. Banyan BIOS
  1031. Although there are a small number of problems known to exist when running
  1032. dBASE IV with Banyan Vines 3.1 and 4.0, they are attributed to the lack of
  1033. full Net BIOS support in the software.
  1034.  
  1035. However, it is possible to setup the Banyan network to fully support the
  1036. NetBios protocol.  All known problems will disappear once the Banyan
  1037. network is setup properly for NetBios support.
  1038.  
  1039. There are three steps to setting up Banyan with the full NetBios support:
  1040.  
  1041. 1.  Create a "NetBios" Service on the server by running the Banyan MSERVICE
  1042. program.  This program sets up NetBios emulation protocol across the LAN
  1043. and checks for unique node addresses.
  1044.  
  1045. 2.  Run the PCNETB.COM program on the workstation.  This program loads the
  1046. NetBios protocol.
  1047.  
  1048. 3.  Run the SETNETB.COM program on the workstation.  This program assigns a
  1049. name to the workstation.
  1050.  
  1051. Adding the NetBios support will up take about 37KB of memory on top of what
  1052. is already loaded.  For example, on the server, run the MSERVICE program
  1053. and set the NetBios service name to something like "NETB@Exec@OURCOMP".  On
  1054. the workstation, add the following to the Autoexec.BAT:
  1055.  
  1056. PCNETB
  1057. SETNETB /N:nodename NETB@Exec@OURCOMP
  1058.  
  1059. where nodename is your unique workstation name.  To be able to set NETBIOS
  1060. for Vines 4.01 you need to load the TSR using the syntax below.    
  1061.  
  1062. "SETNETB /NAME:nodename NETB@Exec@OURCOMP"  
  1063.  
  1064. One final observation: this TSR can't be loaded high.  
  1065.  
  1066.  
  1067.  
  1068. 6  Corrigenda
  1069.  
  1070. Corrigenda
  1071.  
  1072. In our February 1991 issue, the code included in the article "Easy .BINs in
  1073. C"  contained some errors related to the placement of underscores and
  1074. dashes.  Please note that in any case where the global variables argc or
  1075. argv are mentioned, each should be immediately preceded by an underscore,
  1076. thus appearing as _argc or _argv.  Additionally, the last code line in
  1077. Reverse!.C shows the _argc variable being followed by a minus sign.  The
  1078. line should appear as 
  1079.  
  1080. strrev(_argv[_argc--]);
  1081.  
  1082. The same scenario is applied in DD.C on page 18.  In the last clause, the
  1083. double minus sign was again omitted on two lines.  The code should actually
  1084. read
  1085.  
  1086. if (negative)
  1087.     string[--len] = '-';
  1088. while (len)
  1089.     string[--len] = ' ';
  1090.  
  1091.  
  1092. 7  Desktop Calculator
  1093.  
  1094. Desktop Calculator
  1095. Dan Madoni
  1096.  
  1097. Isn't it incredible how rapidly things change?  It wasn't that long ago
  1098. when the idea of personal computing was not nearly as matter-of-fact as we
  1099. regard the activity today.  Seems like it was around that same time that
  1100. the throw-away calculator that you get with a subscription to Time used to
  1101. cost a pretty penny.
  1102.  
  1103. I am always amused to walk into the office of a power executive and find
  1104. that same throw-away calculator sitting next to a new 33MHz Super Dynamo
  1105. 386 computer.  What's the point?  Well, maybe the calculator is used to
  1106. calculate the ultimate numbers to be entered into a database.  Wouldn't it
  1107. be great to be able to pop up a "video calculator" that does all that basic
  1108. arithmetic??
  1109.  
  1110. The Calc() function (see listing) does just that.  Suppose you're sitting
  1111. in a numeric field, and the only thing keeping you from punching in the
  1112. numbers is the fact that you don't know which numbers to punch ─ they
  1113. haven't been calculated.  You press a function key and up pops this
  1114. calculator on the screen that works the same way as the pocket calculator
  1115. on your desk (or in your pocket.)  You punch in the numbers on the video
  1116. calculator via the number pad on your keyboard and watch the result appear
  1117. after each calculation on the LED-style display.  Pressing Escape erases
  1118. the calculator and returns you to your prior activity, which is left
  1119. unaffected.
  1120.  
  1121. After copying the Calc() UDF code below into your program, include the
  1122. following command in your program:
  1123.  
  1124. ON KEY LABEL F5 ?? Calc()
  1125.  
  1126. Whenever you need to use the calculator, press F5, and it will appear in
  1127. the middle of the screen.  Use the number pad for numbers.  All your
  1128. operators for addition, subtraction, multiplication and division are on the
  1129. keypad of most computer keyboards as well.  The asterisk is used for
  1130. multiplication rather than an X.  All other operator symbols should be
  1131. familiar.  You use the Enter key to get the result, taking the place of the
  1132. equal sign. 
  1133.  
  1134.  When you are in the process of doing calculations, the number you are
  1135. working with is accumulative until you press the CLEAR button, ("C" on the
  1136. keyboard.)  If you make a mistake while entering a number, use the
  1137. left-arrow key to delete the right-most number.  Of course, you can use
  1138. another key other than F5.  The function key F5 happens to be a key that
  1139. does not disable other full-screen functions. 
  1140.  
  1141. Function: Calc
  1142. FUNCTION Calc
  1143.  
  1144. *─ Record current environment.
  1145. B4color = SET("ATTRIBUTES")  
  1146. B4curs = SET("CURSOR")       
  1147. B4dec = SET("DECIMAL")
  1148.  
  1149. SET CURSOR OFF               
  1150. SET DECIMAL TO 2             
  1151. SAVE SCREEN TO B4calc        
  1152. tempclr = SET("ATTRIBUTES")
  1153.  
  1154. *─ Draw the calculator.
  1155. SET COLOR TO N/W
  1156. @ 8,32 FILL TO 18,50 COLOR W/N
  1157. @ 7,31 CLEAR TO 17,49
  1158. SET COLOR TO &tempclr
  1159.  
  1160. *─ "Buttons" for calculator.                         
  1161. @ 10,33 SAY " 7 " COLOR W+/N 
  1162. @ 10,37 SAY " 8 " COLOR W+/N 
  1163. @ 10,41 SAY " 9 " COLOR W+/N 
  1164. @ 10,45 SAY " + " COLOR Gr+/N
  1165.                              
  1166. @ 12,33 SAY " 4 " COLOR W+/N 
  1167. @ 12,37 SAY " 5 " COLOR W+/N 
  1168. @ 12,41 SAY " 6 " COLOR W+/N 
  1169. @ 12,45 SAY " - " COLOR Gr+/N
  1170.                              
  1171. @ 14,33 SAY " 1 " COLOR W+/N 
  1172. @ 14,37 SAY " 2 " COLOR W+/N 
  1173. @ 14,41 SAY " 3 " COLOR W+/N 
  1174. @ 14,45 SAY " X " COLOR Gr+/N
  1175.                              
  1176. @ 16,33 SAY " 0 " COLOR W+/N 
  1177. @ 16,37 SAY " " + CHR(249) + " " COLOR Gr+/N 
  1178. @ 16,41 SAY " C " COLOR Gr+/N
  1179. @ 16,45 SAY " " + CHR(246) + " " COLOR Gr+/N 
  1180. @ 8,33 SAY SPACE(15) COLOR W/N               
  1181. SAVE SCREEN TO curcalc       
  1182.                              
  1183. DECLARE calcnums[100]  && Numbers stored as they are pressed
  1184. DECLARE calcops[100]   && Operators stored as they are pressed
  1185.                              
  1186. STORE .F. TO decon,calcdone,invkey
  1187. *─ decon    : Turn decimal point on or off
  1188. *─ calcdone : Tells whether or not to keyboard a value after
  1189. *              calculations have been made
  1190. *─ invkey   : Indicates Invalid Key Pressed if .T.
  1191.  
  1192. newnum = .T.   && Indicates new number is on the display
  1193. worknum = "0"  && Number to be displayed
  1194. calccnt = 0    && Number of entries made before "=" is pressed
  1195.                              
  1196. DO WHILE .T.
  1197.   *─ If the user has not pressed an invalid key...
  1198.   IF .NOT. invkey
  1199.     worknum = IIF(LEN(worknum) = 0, "0", worknum)
  1200.     worknum = IIF(LEN(worknum) > 1 .AND. SUBSTR(worknum,1, 1) = "0",;
  1201.       SUBSTR(worknum, 2), worknum) * Remove leading zero
  1202.     worknum = IIF(LEN(worknum) > 13, SUBSTR(worknum, 1, 13), worknum)
  1203.     * Truncate long number
  1204.  
  1205.     RESTORE SCREEN FROM curcalc
  1206.     @ 8,35 SAY SPACE(13 - LEN(worknum)) + worknum COLOR R+/N  && Show
  1207. number
  1208.   ELSE
  1209.     invkey = .F.
  1210.   ENDIF
  1211.  
  1212.   waiting = INKEY(0)
  1213.   *─ Reset if new number
  1214.   IF newnum
  1215.     worknum = ""
  1216.     newnum = .F.
  1217.     decon = .F.
  1218.   ENDIF
  1219.  
  1220.   DO CASE 
  1221.     CASE waiting = 27 .OR. waiting = 23  && ESCape or Ctrl-End
  1222.       EXIT    
  1223.     CASE waiting = 19  && Left-Arrow
  1224.       @ 8,34 SAY CHR(237) COLOR Gr+/N
  1225.       IF LEN(worknum) <> 0
  1226.         decon = IIF(SUBSTR(worknum,LEN(worknum), 1) = ".", .F., .T.)
  1227.     *  Turn decimal off if deleted 
  1228.         worknum = SUBSTR(worknum,1,LEN(worknum) - 1)  && truncate number
  1229.       ENDIF
  1230.  
  1231.     *─ Numbers on keypad
  1232.     CASE waiting = 55
  1233.       @ 10,33 SAY " 7 " COLOR W+/G
  1234.       worknum = worknum + "7" 
  1235.     CASE waiting = 56
  1236.       @ 10,37 SAY " 8 " COLOR W+/G
  1237.       worknum = worknum + "8" 
  1238.     CASE waiting = 57
  1239.       @ 10,41 SAY " 9 " COLOR W+/G
  1240.       worknum = worknum + "9" 
  1241.     CASE waiting = 52
  1242.       @ 12,33 SAY " 4 " COLOR W+/G
  1243.       worknum = worknum + "4" 
  1244.     CASE waiting = 53
  1245.       @ 12,37 SAY " 5 " COLOR W+/G
  1246.       worknum = worknum + "5" 
  1247.     CASE waiting = 54
  1248.       @ 12,41 SAY " 6 " COLOR W+/G
  1249.       worknum = worknum + "6" 
  1250.     CASE waiting = 49
  1251.       @ 14,33 SAY " 1 " COLOR W+/G
  1252.       worknum = worknum + "1" 
  1253.     CASE waiting = 50
  1254.       @ 14,37 SAY " 2 " COLOR W+/G
  1255.       worknum = worknum + "2" 
  1256.     CASE waiting = 51
  1257.       @ 14,41 SAY " 3 " COLOR W+/G
  1258.       worknum = worknum + "3" 
  1259.     CASE waiting = 48
  1260.       @ 16,33 SAY " 0 " COLOR W+/G
  1261.       worknum = worknum + "0" 
  1262.  
  1263.     CASE waiting = 46 .AND. .NOT. Decon  && Decimal point
  1264.       @ 16,37 SAY " " + CHR(249) + " " COLOR W+/G
  1265.       worknum = worknum + "." 
  1266.       decon = .T.
  1267.     CASE STR(waiting,3) $ " 43 13 61"  && ENTER, "=", or "+"
  1268.       @ 10,45 SAY " + " COLOR W+/G
  1269.       newnum = .T.
  1270.       calccnt = calccnt + 1
  1271.       calcnums[calccnt] = VAL(worknum)
  1272.       calcops[calccnt + 1] = "+"
  1273.     CASE waiting = 45
  1274.       @ 12,45 SAY " - " COLOR W+/G
  1275.       newnum = .T.
  1276.       calccnt = calccnt + 1
  1277.       calcnums[calccnt] = VAL(worknum)
  1278.       calcops[calccnt + 1] = "-"
  1279.     CASE CHR(waiting) $ "XX*" 
  1280.       @ 14,45 SAY " X " COLOR W+/G
  1281.       newnum = .T.
  1282.       calccnt = calccnt + 1
  1283.       calcnums[calccnt] = VAL(worknum)
  1284.       calcops[calccnt + 1] = "*"
  1285.     CASE waiting = 47
  1286.       @ 16,45 SAY " " + CHR(246) + " " COLOR W+/G
  1287.       newnum = .T.
  1288.       calccnt = calccnt + 1
  1289.       calcnums[calccnt] = VAL(worknum)
  1290.       calcops[calccnt + 1] = "/"
  1291.     CASE CHR(waiting) $ "Cc"  && The CLEAR button
  1292.       calccnt = 0
  1293.       newnum = .T.
  1294.     OTHERWISE 
  1295.       invkey = .T.  && User pressed an invalid key
  1296.   ENDCASE 
  1297.  
  1298.   IF CHR(waiting) $ "+/-*XX=" .OR. waiting = 13
  1299.     allnum = calcnums[1]  && allnum will be the result of all calculations
  1300.     calccnt2 = 1
  1301.  
  1302.     *─ The following DO WHILE loop takes all of the numbers and
  1303.     *─ operators that have been accumulated thus far and
  1304.     *─ calulates them in the order entered.  We can't 
  1305.     *─ concantenate them into one large string to calculate because
  1306.     *─ of the way that dBASE IV orders the precedence of operators.
  1307.     *─ (such as 5 + 5 / 2 calculated from left to right would result
  1308.     *─ differently than as one dBASE expression)
  1309.  
  1310.     DO WHILE calccnt2 < calccnt
  1311.       calccnt2 = calccnt2 + 1 
  1312.       DO CASE 
  1313.         CASE calcops[calccnt2] = "+"
  1314.           allnum = allnum + calcnums[calccnt2]
  1315.         CASE calcops[calccnt2] = "-"
  1316.           allnum = allnum - calcnums[calccnt2]
  1317.         CASE calcops[calccnt2] = "*"
  1318.           allnum = allnum * calcnums[calccnt2]
  1319.         CASE calcops[calccnt2] = "/"
  1320.           allnum = allnum / calcnums[calccnt2]
  1321.       ENDCASE 
  1322.     ENDDO
  1323.  
  1324.     @ 8,33 SAY SPACE(15) COLOR W/N
  1325.     @ 8,35 SAY STR(allnum,13,2) COLOR R+/N  && Show the result
  1326.  
  1327.     STORE .T. TO calcdone,newnum
  1328.     waiting = INKEY(0)
  1329.     KEYBOARD CHR(waiting)  && Keyboard next number and clear for new number
  1330.   ENDIF
  1331.  
  1332.   IF .NOT. calcdone  && If number is still building on display
  1333.     IF .NOT. invkey
  1334.       SET BELL TO 5000,1  && Good tone if key is not invalid
  1335.     ELSE 
  1336.       SET BELL TO 20,1  && Bad tone if key is invalid
  1337.     ENDIF
  1338.     ?? CHR(7) 
  1339.   ELSE
  1340.     calcdone = .F.
  1341.     invkey = IIF(.NOT. CHR(waiting) $ "1234567890+-Cc*/",.T., invkey)
  1342.     *  Determine valid key pressed
  1343.   ENDIF
  1344. ENDDO
  1345.  
  1346. RESTORE SCREEN FROM B4calc
  1347. RELEASE SCREEN B4calc
  1348. RELEASE SCREEN curcalc
  1349. RELEASE calcnums,calcops
  1350.  
  1351. SET COLOR TO &B4color
  1352. SET DECIMAL TO B4dec
  1353. SET CURSOR &B4curs
  1354.  
  1355.  
  1356. RETURN .T.
  1357. * EOF: Calc.PRG  
  1358.  
  1359.  
  1360.  
  1361. 8  Betcha Can't Pick Just One
  1362.  
  1363. Betcha Can't Pick Just One
  1364. Lena Tjandra
  1365.  
  1366. Popups are a welcome addition to the functionality of the dBASE language. 
  1367. By their nature, once you press Return on a highlighted selection in the
  1368. popup, it disappears and an action is taken.  You cannot make more than one
  1369. selection at a time, but there is an alternate and sometimes preferable way
  1370. of utilizing a popup.   
  1371.  
  1372. Suppose you have a list of salespersons, and you would like to generate a
  1373. year-to-date sales report on specific salespersons only.   You'd ideally
  1374. like to go through a popup list, pressing Return (or some other key to mark
  1375. the records) on those you want selected for a certain operation.  When you
  1376. are done, a specific exit key would clear the popup and perform your task. 
  1377. How would you do this and still incorporate the use of a popup?  The
  1378. program that follows shows you how.
  1379.  
  1380. The purpose of this program is to create a popup from which the user can
  1381. make multiple selections.  The popup is created from values stored in a
  1382. database.  When the user makes a selection, the popup is redefined with a
  1383. character marker that indicates a selection.  At the same time, a logical
  1384. true (.T.) value is replaced into the field, "Selected" which corresponds
  1385. to the selected option in the database.  If an option on the popup is
  1386. de-selected, then a logical false (.F.) value is replaced into the
  1387. "Selected" field and the popup is redefined without the character marker.
  1388.  
  1389. If your popup contains a lot of options to choose from, then your best bet
  1390. is to store them in a database and create the popup based on the values in
  1391. that database.  There are several good reasons for doing this such as code
  1392. brevity, modularity, and clarity.  For instance, instead of having to
  1393. DEFINE BARs for each option in the popup, you can define all the BARs in
  1394. just a few statements as shown on lines 19 - 22.  Second, it makes the
  1395. program more modular because  now you can modify the options through the
  1396. database rather than through the program, thus, giving your program more
  1397. flexibility.  Third, you can create an additional field in the database to
  1398. store marked selections, thus, saving you from declaring an array of
  1399. unnecessary size.  Furthermore, it makes the goal easier to process. 
  1400. Suppose you stored selected values in an array.  The consequence would be
  1401. having to look in each element to see which options have been selected and
  1402. create a macro expression for the report.  If the values were marked in the
  1403. database by assigning .T. to a field called SELECTED, then a single command
  1404. will suffice:
  1405.  
  1406. REPORT FORM sales FOR selected
  1407.  
  1408. I would also like to point out a few tips contained in this program.  You
  1409. may  wonder why a SHOW POPUP on line 35 is issued before an ACTIVATE
  1410. POPUP.  For those of you who have worked with popups a lot, you may notice
  1411. that the popup will flicker very quickly when control is returned to the
  1412. ACTIVATE POPUP statement or when the popup is re-activated.  By issuing a
  1413. SHOW POPUP, we can alleviate the flickers.
  1414.  
  1415. Lines 53 - 54 may seem peculiar, but since the database has an active
  1416. index, we can only move to the record relative to the top of the database. 
  1417. However,  if the database were in natural order, we can reference it
  1418. directly with a  GOTO <record no> statement.  Another method is to do a
  1419. SEEK, but doing a SKIP is the only way that works with both an indexed or
  1420. un-indexed database.
  1421.  
  1422. Additionally, you may also notice that the F9 and F10 keys have been
  1423. defined  as hotkeys to allow the user the choice of selecting or
  1424. de-selecting all the  options on the popup at once.  This brings me to one
  1425. final point.  In the procedure entitled PSelect, the bar is redefined, but
  1426. it is not necessary to deactivate and reactivate the popup.  Since this
  1427. procedure was called from an ON SELECTION POPUP, the ON SELECTION takes
  1428. care of reactivating the popup with the current bar definitions.  On the
  1429. other hand, the procedure Sel All was called from an ON KEY, so we need to
  1430. deactivate the popup manually as on line 77, and reactivate it again in
  1431. order for the new bars to display. 
  1432.  
  1433.  1 CLEAR
  1434.  2 SET STATUS OFF
  1435.  3 SET TALK OFF
  1436.  4  
  1437.  5 *─ Store ASCII value of {down-arrow} and {Esc} to variables
  1438.  6 dnarrow = CHR(24)
  1439.  7 escape = 27
  1440.  8 
  1441.  9 *─ Open database
  1442. 10 SELECT 1
  1443. 11 USE salesrep ORDER name
  1444. 12 
  1445. 13 *─ Define popup, pop1
  1446. 14 DEFINE POPUP pop1 FROM 5, 15 TO 23, LEN(name) + 15
  1447. 15 
  1448. 16 bar cnt = 1
  1449. 17 
  1450. 18 *─ Define bars of popup from the "name" field in database
  1451. 19 SCAN
  1452. 20  DEFINE BAR bar cnt OF pop1 PROMPT TRIM(Name) + IIF(selected, CHR(17),
  1453. "")
  1454. 21  bar cnt = bar cnt + 1
  1455. 22 ENDSCAN
  1456. 23 
  1457. 24 *─ Define popup action
  1458. 25 ON SELECTION POPUP pop1 DO PSelect
  1459. 26 
  1460. 27 *─ Define hotkey <F10> for selecting all options
  1461. 28 ON KEY LABEL F10 DO Sel All WITH .T.
  1462. 29 
  1463. 30 *─ Define hotkey <F9> for de-selecting all options
  1464. 31 ON KEY LABEL F9 DO Sel All WITH .F.
  1465. 32 
  1466. 33 *─ Activate popup
  1467. 34 DO WHILE LASTKEY() <> escape 
  1468. 35   SHOW POPUP pop1
  1469. 36   ACTIVATE POPUP pop1
  1470. 37 ENDDO
  1471. 38 
  1472. 39 CLEAR
  1473. 40 CLEAR ALL
  1474. 41 SET STATUS ON
  1475. 42 SET TALK ON
  1476. 43 
  1477. 44 
  1478. 45 PROCEDURE pselect
  1479. 46 
  1480. 47 *─ Redefine bar based on selection/de-selection
  1481. 48 DEFINE BAR BAR() OF pop1 PROMPT IIF(RIGHT(PROMPT(), 1) = CHR(17),;
  1482. 49   SUBSTR(PROMPT(), 1, AT(CHR(17), PROMPT()) - 1), PROMPT() + CHR(17))
  1483. 50 
  1484. 51 *─ Change the "Selected" field to .T. if option has been selected 
  1485. 52 *─ or .F. if option has been de-selected.
  1486. 53 GO TOP
  1487. 54 SKIP BAR() - 1
  1488. 55 REPLACE selected WITH IIF(selected, .F., .T.)
  1489. 56 
  1490. 57 *─ Move highlight in popup down to the next option
  1491. 58 KEYBOARD dnarrow
  1492. 59 
  1493. 60 RETURN
  1494. 61 
  1495. 62 PROCEDURE Sel All
  1496. 63 
  1497. 64 PARAMETER s key
  1498. 65 
  1499. 66 *─ Change all values in the "Selected" field .T. or .F. in database
  1500. 67 REPLACE ALL Selected WITH s key
  1501. 68 
  1502. 69 bar cnt = 1
  1503. 70 
  1504. 71 *─ Re-define bars of popup from the "Name" field in database
  1505. 72 SCAN
  1506. 73   DEFINE BAR bar cnt OF pop1 PROMPT TRIM(name) + IIF(selected, CHR(17),
  1507. "")
  1508. 74   bar cnt = bar cnt + 1
  1509. 75 ENDSCAN
  1510. 76 
  1511. 77 DEACTIVATE POPUP
  1512. 78 
  1513. 79 RETURN
  1514.  
  1515.  
  1516.  
  1517. 9  Support That Never Sleeps
  1518.  
  1519. Support That Never Sleeps
  1520.  
  1521. Introducing the newest member of our software support team:  
  1522. Auto-Tate for dBASE IV.  
  1523.  
  1524. Auto-Tate is an automated audio support system, that is accessed via a
  1525. touch tone telephone, capable of handling up to 16 callers simultaneously. 
  1526. It is a system designed to "remember" all of the choices each caller makes
  1527. and route them to the information that pertains specifically to them.  It
  1528. will also allow callers to "save" their place so they can hang up and call
  1529. back, even days later, and pick up right where they left off.  Best of all,
  1530. this great new service is toll-free!
  1531.  
  1532. In this "Age of Information", people have a high level of expectation when
  1533. it comes to response time, availability, and content of the information
  1534. they seek.  Ashton-Tate's software support department is always looking for
  1535. new ways to meet these expectations.
  1536.  
  1537. Employing automated voice technology allows us to offer our customers many
  1538. new advantages, the most obvious being that it's toll free, 24 hours a day,
  1539. 7 days a week.  This means you can get help with problems at night and on
  1540. weekends but, perhaps, more importantly, you can spend time learning from
  1541. Auto-Tate at your convenience, which is not always during your busy work
  1542. day.
  1543.  
  1544. Beyond convenience, Auto-Tate provides a resource that can be modified and
  1545. updated continually, it can grow and change in a way that no manual or
  1546. written word can.  Imagine going back to using a typewriter instead of the
  1547. word processor you now have; if one change is required, it means having to
  1548. re-type the entire page!  Manuals have the same limitation and therefore
  1549. must be written in less specific terms in order to retain validity over a
  1550. longer period of time.  With Auto-Tate we can address the areas that seem
  1551. the most confusing to our customers and be quite specific, getting right to
  1552. the point.
  1553.  
  1554. Our technicians continually relay what topics concern our customers the
  1555. very most.  Currently, two very asked-about subjects are installation and
  1556. printing.  With Auto-Tate, you can approach these topics from two angles:
  1557. by listening to tutorial information or troubleshooting a specific
  1558. problem.  You can do a "walk through" for background and preparation, or
  1559. you can select an error message or symptom you have enountered, and listen
  1560. to the probable cause and solution.
  1561.  
  1562. For example: you are ready to install version 1.1, and you have a lot of
  1563. questions.  A walk-thru will tell you what to expect at each step and what
  1564. you need to do (or not do as the case me be).
  1565.  
  1566. But suppose you've already installed and done everything correctly, only
  1567. the program won't start.  Troubleshooting will take you through the same
  1568. steps a technician would, telling you where to look for the problem and
  1569. giving suggestions or alternatives.
  1570.  
  1571. The tutorials and troubleshooting are designed by technicians with years of
  1572. phone support experience; they know the questions customers really ask, and
  1573. they know how to break down the answers into logical steps.  Each subject
  1574. is handled by a technician who has specialized in that particular area,
  1575. giving you the benefit of many technicians' knowledge at once.
  1576.  
  1577. There's a feeling that some customers get when they know the answer is
  1578. somewhere in their manuals, but they're pressed for time and just need
  1579. someone to direct them to the right spot.  Both tutorial and
  1580. troubleshooting will refer you to specific pages in the documentation where
  1581. you can get more information.
  1582.  
  1583. How To Use It
  1584.  
  1585. Auto-Tate consists of two parts: a voice tree and a diagnostic.  The voice
  1586. tree is something you are most likely familiar with these days if you call
  1587. any type of service organization.  You select a topic from a menu and
  1588. follow directions to obtain more information.  You can return to the menu
  1589. and make a different choice if you wish at any step.
  1590.  
  1591. The diagnostic is quite different, because it is leading you through a
  1592. progression of informative steps based on the responses you make. 
  1593. Sometimes you will select from a menu of choices, other times you will
  1594. simply answer "yes" or "no".  If you feel you have made an incorrect
  1595. response or need to repeat something, you press the * (asterisk) to backup
  1596. one step.
  1597.  
  1598. In a diagnostic session, you will often be asked to check or try something,
  1599. which may take time, so you can "save" your session by pressing "00". 
  1600. Auto-Tate will assign you a session number, then you can hang up and go do
  1601. what you need to do. Call back any time and resume the session by entering
  1602. your session number when prompted.
  1603.  
  1604. Suppose you're having printing problems.  When you attempt to print,
  1605. nothing comes out of the printer.  Auto-Tate will ask if you can print from
  1606. a DOS level to determine if you're having a problem within dBASE IV
  1607. specifically or with your hardware environment.  Auto-Tate will tell you
  1608. exactly what to type on your computer to test the printing capability of
  1609. your computer from a DOS level.  Sometimes, you may not be in a position to
  1610. try the suggestions at the time of your call to Auto-Tate.  Should that be
  1611. the case, press 00 to save your place in the session, hang up and try the
  1612. suggestions when you're in front of your computer!  If the results are not
  1613. conclusive and you need more information, when you call Auto-Tate again,
  1614. you'll pick up right where you left off!
  1615.  
  1616. Once you're in diagnostic mode, you can save your session anytime, even if
  1617. you just need to take another call, go to a meeting, or if you're just not
  1618. in the mood anymore.  Auto-Tate will be there when you come back, saving
  1619. your place so you don't have to remember where you were or listen to the
  1620. same recorded prompts repeatedly.
  1621.  
  1622. The following diagram illustrates the menu access for Auto-Tate.  As you
  1623. can see, only 1 and 2 take you to diagnostics, the other selections behave
  1624. in regular voice tree manner, meaning you can return to the menu and choose
  1625. something else.
  1626.  
  1627. Choosing the Different Options
  1628.  
  1629. The present offerings are exclusively for dBASE IV 1.1.  Choosing the first
  1630. option begins a diagnostic session for tutorial or troubleshooting dBASE IV
  1631. version 1.1.
  1632.  
  1633. Choosing option 2, "Resume Previous Session", is for those users who have
  1634. previously been in a diagnostic session and have saved their place they now
  1635. wish to resume.  Auto-Tate prompts you to enter your session number it
  1636. assigned during your last phone call on the subject.
  1637.  
  1638. The third option is "Hardware Certification List".  Aston-Tate has
  1639. certified various PCs, printers and Local Area Networks for use with dBASE
  1640. IV version 1.1.  Here, you can find out if your equipment has been tested
  1641. and is approved for use with our software.
  1642.  
  1643. Option #4, "Bulletin Board System" gives phone numbers and brief
  1644. instructions for accessing and downloading files on the Ashton-Tate
  1645. Bulletin Board System and CompuServe.  Public domain or shareware programs
  1646. such as PKZIP are explained since you may need one or more of these
  1647. utilities to use the files you have downloaded.
  1648.  
  1649. The option entitled "This week's Top Five" is intended to give a quick look
  1650. at the top five support topics of the week.  Included could be
  1651. announcements, helpful hints, common problems (and their solutions);
  1652. anything Software Support thinks could be of interest to dBASE IV users and
  1653. developers.  This information will change weekly, so you can check
  1654. routinely.
  1655.  
  1656. We always include the option to bypass the introduction and instructions,
  1657. for those repeat users who are familiar with the system but we do recommend
  1658. you listen to them the first time around.  You'll want to become familiar
  1659. with how to navigate through the system.
  1660.  
  1661. In all categories, the information will be updated on a regular basis,
  1662. evaluating what customers are calling about and adding or updating
  1663. information.  So, you have a dynamic, efficient and constantly available
  1664. new medium for obtaining support on dBASE IV.  We've taken great strides to
  1665. create this system and have much in store for this service in the future. 
  1666. We feel it will be a tremendous help to our customers getting up and
  1667. running with dBASE IV.  Auto-Tate is going to make a great contribution to
  1668. our support team.  In a sense, we've hired the ideal technician: efficient,
  1669. listens well, never gets tired or bored and remembers everything.  But most
  1670. importantly to our support staff, he never hogs the pizza!
  1671.  
  1672.  
  1673.