home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / PROG_C / RECIO110.ZIP / TIPS.TXT < prev    next >
Text File  |  1994-03-28  |  10KB  |  285 lines

  1.     Title: RECIO TIPS
  2. Copyright: (C) 1994 William Pierpoint
  3.   Version: 1.10
  4.      Date: March 28, 1994
  5.  
  6.  
  7.  
  8. 1.0 COMMON PROGRAMMING ERRORS
  9.  
  10. 1.1 Inadvertently typing a stdio function instead of a recio function
  11.  
  12. Many functions in recio and stdio have similar names and similar uses.  
  13. This makes it easier to learn the recio functions, but it also makes it 
  14. easier to unintentionally type in a stdio function when you really want a 
  15. recio function.  When compiling it is best to have all warnings turned on.  
  16. A mistyped function will give a "suspicious pointer conversion" warning; 
  17. a mistyped macro will give an "undefined symbol" error.  The one exception: 
  18. no error or warning if you type fcloseall() instead of rcloseall().
  19.  
  20. 1.2 Inadvertently typing the wrong symbolic name for an error constant
  21.  
  22. Symbolic names for error constants are similar between recio and those 
  23. supported by the compiler.  You may have typed ENOMEM when you meant 
  24. R_ENOMEM.  Check to make sure that for valid record pointers, symbolic 
  25. error constants start with R_E; for invalid record pointers, symbolic error 
  26. constants start with E.
  27.  
  28. 2.0 ERROR HANDLING
  29.  
  30. 2.1 Callback Error Function
  31.  
  32. The first use of any recio function should be rseterrfn() to register 
  33. your callback error function for your application.
  34.  
  35.  
  36. 2.2 Explicit Conditions Not Reported to Callback Error Function
  37.  
  38. There are two conditions that your code must explicitly handle as 
  39. these are not reported as errors to the callback error function:
  40.  
  41. 1)  Test ropen() for NULL return and, if true, test errno for ENOENT.  
  42. This indicates that the file could not be opened since it does not exist.  
  43. Any other errors are reported to the callback error function (if registered); 
  44. your code can handle them there.
  45.  
  46.     REC *rp = ropen("file", "r");
  47.     if (!rp) {
  48.         if (errno == ENOENT) {
  49.         /* file does not exist */
  50.         ...
  51.         }
  52.     }
  53.  
  54. 2)  Test the return value from rgetrec() to see if you have reached the end 
  55. of the file.  If it is a NULL return, then either end of file reached or an 
  56. error occured.  If you installed a callback error function that is able to 
  57. correct all errors from rgetrec(), then the NULL return only indicates end 
  58. of file.  But in general you need to test reof() to see if end of file was 
  59. reached (or rerror() to see if error occurred).  Any errors would have been 
  60. reported to the callback error function; be careful that you don't report 
  61. the same error twice.
  62.  
  63.     /* loop through all records in file */
  64.     while (rgetrec(rp)) {
  65.         ...
  66.     }
  67.     if (!reof(rp)) {
  68.         /* error occurred before all records read */
  69.         ...
  70.     }
  71.  
  72.  
  73. 2.3 Make an Error Check Just Prior to the rclose Function
  74.  
  75. Check for errors just before closing any record stream.  This is a good 
  76. safety check since (1) you might have forgotten to install your callback 
  77. error function or (2) your callback error function failed to catch and 
  78. correct all the errors.
  79.  
  80.     if (rerror(rp)) {
  81.         /* file not completely read in */
  82.         ...
  83.     }
  84.     rclose(rp);
  85.  
  86. If you use recin in your program, check for errors after your last use of 
  87. recin or just before you exit your program.
  88.  
  89.     if (rerror(recin)) {
  90.         /* error occurred on recin stream */
  91.         ...
  92.         exit(EXIT_FAILURE);
  93.     }
  94.     exit(EXIT_SUCCESS);
  95.  
  96.  
  97. 2.4 The rsetfldstr Function Clears Error and End-of-File Indicators
  98.  
  99. The rsetfldstr function has a side effect in that it internally calls 
  100. the rclearerr function, which clears the error and end-of-file indicators.  
  101. The rationale for this is as follows:  
  102.  
  103.     1. The rsetfldstr function is used in the callback error function to 
  104.        correct data errors.  Even if used elsewhere it's purpose is to 
  105.        force-feed a data value to the program.
  106.        
  107.     2. When the callback error function returns, the recio library 
  108.        functions will only read the replacement value if the error 
  109.        and end-of-file indicators are clear.
  110.     
  111.  
  112.  
  113. 3.0 FIELDS
  114.  
  115. 3.1 Field Delimiters
  116.  
  117. Field separator and text delimiter characters must be ASCII, but not
  118. the null character as the C language uses it to mark the end of a string.
  119.  
  120. If a delimiter is set to the space character, it is taken to mean any
  121. white space: space, tab, etc.  See the documentation that comes with your 
  122. compiler for the isspace() function.
  123.  
  124. Delimiters around text are optional; no error is generated if they are 
  125. missing.  However text delimiters are needed if the string contains a 
  126. field separator character.  
  127.  
  128.  
  129. 3.2 String Fields
  130.  
  131. Empty strings are legal; no error is generated if there is nothing in a 
  132. string field.  All other types of fields must have something in them or 
  133. a missing data error is generated.
  134.  
  135. If you do this:
  136.  
  137.     /* usually bad */
  138.     char *strptr = rgets(rp);
  139.  
  140. strptr points to the string buffer which changes every time a new field 
  141. is read.  Instead copy the data into your string.  But the method below 
  142. could truncate your data if the field buffer has expanded.
  143.  
  144.     char str[FLDBUFSIZ+1];
  145.     
  146.     /* could lose data if field buffer expands */
  147.     strncpy(str, rgets(rp), FLDBUFSIZ);
  148.     str[FLDBUFSIZ] = '\0';
  149.  
  150. Instead you will need to dynamically allocate memory space for your strings.  
  151. If you look at TEST.C, you will find the scopys function, which you can use 
  152. for dynamic string copying.  If you use scopys, don't forget to (1) set all 
  153. string pointers to NULL when declaring them, and (2) free your strings when 
  154. finished with them.
  155.  
  156.     char *str=NULL;
  157.     
  158.     scopys(str, rgets(rp));
  159.     ...
  160.     free(str);
  161.     return EXIT_SUCCESS;
  162.  
  163.  
  164.  
  165. 4.0 FINE TUNING
  166.  
  167. 4.1 Better Use of Heap Space
  168.  
  169. If you are tight on memory, you can fine tune recio for your application 
  170. by doing the following:
  171.  
  172. 1. Set ROPEN_MAX to the minimum number of files you need open simultaneously.
  173.    Note that recin is always open and must be included in the count.
  174.  
  175. 2. Use rsetfldsiz() and rsetrecsiz() functions to set the maximum size 
  176.    record and field needed for that record stream.  Use these functions 
  177.    before the first field or record is read from the file.  To determine 
  178.    the maximum size record buffer, determine the number of characters in 
  179.    the longest line of the file to be read, including the newline.  To 
  180.    determine the maximum size field buffer, determine the number of 
  181.    characters in the longest field in the record.  If the longest field 
  182.    contains text delimiters, a trailing field delimiter, or white space 
  183.    between the trailing text delimiter and the trailing field delimiter, 
  184.    include these as part of the size.
  185.  
  186.  
  187.  
  188. 5.0 IDEAS FOR EXPANDED CAPABILITIES
  189.  
  190.  
  191. 5.1 Additional Types of Input Functions
  192.  
  193. The macros rget_fn, rcget_fn, etc are used to define functions that get 
  194. numerical input.  By developing the appropriate conversion functions, 
  195. one could expand recio to get other types of data, such as time, date, 
  196. etc.
  197.  
  198.  
  199. 5.2 Wrapper Functions and Macros
  200.  
  201. If you define wrapper functions or macros that supply a default value 
  202. when the record pointer is NULL, then you can combine reading the file or 
  203. reading a set of default values with the same section of code.
  204.  
  205. REC *rp = ropen("file", "r");
  206. if (rp || (!rp && errno==ENOENT)) {
  207.     ...
  208.     if (rp) rclose(rp);
  209. }
  210.  
  211. 5.2.1 Default Value
  212.  
  213. If your application cannot find a data file, you may want to use a set of
  214. built-in default values.  This could be a good strategy if your application
  215. uses configuration files.  Note that wrappers will be needed on almost every 
  216. recio function that occurs after the ropen function, including rclose, 
  217. rsetrecsiz, rsetfldsiz, rsetfldch, and rsettxtch, to prevent reporting a
  218. null record pointer to your callback error function.
  219.  
  220. Example Function
  221.  
  222. The rdgeti function gets an integer from an opened data file or gets the
  223. default value if the data file has not been opened (NULL pointer).
  224.  
  225. /* if file open, read value from file; else use default value */
  226. int rdgeti(REC *rp, int default) {
  227.     return (rp ? rgeti(rp) : default);
  228. }
  229.  
  230. Example Macro
  231.  
  232. You can easily rewrite the rdgeti function as a macro.
  233.  
  234. #define rdgeti(rp, default) ((rp) ? rgeti(rp) : (default))
  235.  
  236.  
  237. 5.2.2 Validated Range
  238.  
  239. In order to validate data, you need to make certain that the value read
  240. from the file is within established limits.  You may want to add functions 
  241. that post the range values to an internal data clipboard which your callback 
  242. error function can access.  If you are letting users correct data on the 
  243. fly, convert the minimum and maximum values to strings, and post pointers 
  244. to the strings.
  245.  
  246. Example Function
  247.  
  248. The rrgeti function gets a integer and validates that the integer is within
  249. the established range.
  250.  
  251. int rrgeti(REC *rp, int min, int max) {
  252.     int result;
  253.     
  254.     result = rgeti(rp);
  255.     if (result < min || result > max) {
  256.         rseterr(rp, R_ERANGE);
  257.     }
  258.     return result;
  259. }
  260.  
  261.  
  262. 5.2.3 Default and Range
  263.  
  264. You may want to combine default value and range validation into one function.
  265.  
  266. Example Function
  267.  
  268. The rdrgeti function gets an integer from an opened data file or gets the
  269. default value if the data file has not been opened.  The function validates 
  270. that the integer is within the established range.
  271.  
  272. int rdrgeti(REC *rp, int default, int min, int max) {
  273.     int result;
  274.  
  275.     if (!rp) {
  276.         result = default;
  277.     } else {
  278.         result = rgeti(rp);
  279.     }
  280.     if (result < min || result > max) {
  281.         rseterr(rp, R_ERANGE);
  282.     }
  283.     return result;
  284. }
  285.