home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #16 / NN_1992_16.iso / spool / comp / lang / tcl / 1039 < prev    next >
Encoding:
Text File  |  1992-07-23  |  8.3 KB  |  295 lines

  1. Newsgroups: comp.lang.tcl
  2. Path: sparky!uunet!zaphod.mps.ohio-state.edu!pacific.mps.ohio-state.edu!linac!att!cbnewsm!grenache!gah
  3. From: gah@grenache (George A. Howlett)
  4. Subject: Tcl 6.3 "expr" patch to add "OFMT" variable 
  5. Reply-To: george.howlett@att.com
  6. Organization: AT&T Bell Laboratories
  7. Date: Thu, 23 Jul 1992 16:26:59 GMT
  8. Message-ID: <1992Jul23.162659.4183@cbnewsm.cb.att.com>
  9. X-Newsreader: Tin 1.1 PL4
  10. Sender: news@cbnewsm.cb.att.com (NetNews Administrator)
  11. Nntp-Posting-Host: grenache.cnet.att.com
  12. Lines: 281
  13.  
  14. This patch adds a global Tcl variable "OFMT" which controls the
  15. conversion format of floating point values from the "expr" command.
  16. The default value is "%g" (what is currently in tclExpr.c).  This
  17. means that existing code should perform as expected.  New values for
  18. "OFMT" must be valid format strings to convert floating point values.
  19. There can be no auxiliary width or precision arguments (using "*").
  20. There can be no extra characters.  If "OFMT" is unset, the value of
  21. "OFMT" reverts back to the default.
  22.  
  23. This patch is baselined on the distributed verion of tclExpr.c and
  24. tclBasic.c in Tcl 6.3.  Mail comments or bug reports back to
  25. "george.howlett@att.com".
  26.  
  27. --gah 
  28.  
  29. *** tclBasic.c-dist    Mon Feb 10 12:29:43 1992
  30. --- tclBasic.c    Mon Jun 22 15:18:34 1992
  31. ***************
  32. *** 130,135 ****
  33. --- 130,136 ----
  34.   Tcl_Interp *
  35.   Tcl_CreateInterp()
  36.   {
  37. +     extern Tcl_VarTraceProc OutputFormatProc;
  38.       register Interp *iPtr;
  39.       register Command *cmdPtr;
  40.       register CmdInfo *cmdInfoPtr;
  41. ***************
  42. *** 193,199 ****
  43.   #ifndef TCL_GENERIC_ONLY
  44.       TclSetupEnv((Tcl_Interp *) iPtr);
  45.   #endif
  46.       return (Tcl_Interp *) iPtr;
  47.   }
  48.   
  49. --- 194,202 ----
  50.   #ifndef TCL_GENERIC_ONLY
  51.       TclSetupEnv((Tcl_Interp *) iPtr);
  52.   #endif
  53. !     Tcl_TraceVar2 ((Tcl_Interp *) iPtr, "OFMT", (char *)NULL, 
  54. !            (TCL_TRACE_WRITES | TCL_TRACE_UNSETS), OutputFormatProc, 
  55. !            (ClientData) NULL);
  56.       return (Tcl_Interp *) iPtr;
  57.   }
  58.   
  59. *** tclExpr.c-dist    Mon Mar 23 12:54:06 1992
  60. --- tclExpr.c    Thu Jul 23 12:07:53 1992
  61. ***************
  62. *** 157,162 ****
  63. --- 157,176 ----
  64.       "-", "!", "~"
  65.   };
  66.   
  67. + /* Default format conversion string */
  68. + static char defFmt[] = "%g";
  69. + /* 
  70. +  * Floating point output format conversion string.
  71. +  *
  72. +  * This probably should be part of the interpreter itself (as a
  73. +  * Tcl or C variable), so that multiple interpreters can have 
  74. +  * multiple formats. 
  75. +  *
  76. +  */
  77. + static char *OFMT = defFmt;
  78.   /*
  79.    * Declarations for local procedures to this file:
  80.    */
  81. ***************
  82. *** 1124,1130 ****
  83.       if (valuePtr->type == TYPE_INT) {
  84.       sprintf(valuePtr->pv.buffer, "%ld", valuePtr->intValue);
  85.       } else if (valuePtr->type == TYPE_DOUBLE) {
  86. !     sprintf(valuePtr->pv.buffer, "%g", valuePtr->doubleValue);
  87.       }
  88.       valuePtr->type = TYPE_STRING;
  89.   }
  90. --- 1138,1144 ----
  91.       if (valuePtr->type == TYPE_INT) {
  92.       sprintf(valuePtr->pv.buffer, "%ld", valuePtr->intValue);
  93.       } else if (valuePtr->type == TYPE_DOUBLE) {
  94. !     sprintf(valuePtr->pv.buffer, OFMT, valuePtr->doubleValue);
  95.       }
  96.       valuePtr->type = TYPE_STRING;
  97.   }
  98. ***************
  99. *** 1318,1324 ****
  100.       if (value.type == TYPE_INT) {
  101.           sprintf(interp->result, "%ld", value.intValue);
  102.       } else if (value.type == TYPE_DOUBLE) {
  103. !         sprintf(interp->result, "%g", value.doubleValue);
  104.       } else {
  105.           if (value.pv.buffer != value.staticSpace) {
  106.           interp->result = value.pv.buffer;
  107. --- 1332,1338 ----
  108.       if (value.type == TYPE_INT) {
  109.           sprintf(interp->result, "%ld", value.intValue);
  110.       } else if (value.type == TYPE_DOUBLE) {
  111. !         sprintf(interp->result, OFMT, value.doubleValue);
  112.       } else {
  113.           if (value.pv.buffer != value.staticSpace) {
  114.           interp->result = value.pv.buffer;
  115. ***************
  116. *** 1334,1336 ****
  117. --- 1348,1519 ----
  118.       }
  119.       return result;
  120.   }
  121. + /*
  122. +  *----------------------------------------------------------------------
  123. +  *
  124. +  * VerifyFormat --
  125. +  *
  126. +  *      Check the format conversion string used to convert floating
  127. +  *    point numbers.  The given format must be a valid floating
  128. +  *    point conversion string.  There can be no other conversions
  129. +  *    or extra characters in the description.  In addition, it cannot
  130. +  *    have extra width or precision integer arguments (using '*').
  131. +  *
  132. +  * Results:
  133. +  *      A standard Tcl result.
  134. +  *
  135. +  * Side effects:
  136. +  *      See the user documentation.
  137. +  *
  138. +  *----------------------------------------------------------------------
  139. +  */
  140. + static int
  141. + VerifyFormat(interp, newFormat)
  142. +     Tcl_Interp *interp;         /* Interpreter to report errors to */
  143. +     char *newFormat;         /* New conversion format to be tested */
  144. + {
  145. +     register char *format;
  146. +     format = newFormat;
  147. +     if ((*format != '%') || (format[1] == '%')) {
  148. +         interp->result = "format string can't specify non-numeric format";
  149. +     return TCL_ERROR;
  150. +     }
  151. +     format++;
  152. +     /* Zero or more flags, which modify the conversion. */
  153. +     while ((*format == '-') || (*format == '#') || (*format == '+')
  154. +        || (*format == ' ')) {
  155. +           format++;
  156. +     }
  157. +     /* Optional decimal digit string specifying field width... */
  158. +     if (*format == '0') {
  159. +         format++;
  160. +     }
  161. +     if (*format == '*') {
  162. +         interp->result = 
  163. +       "format string can't specify integer (*) width argument";
  164. +     return TCL_ERROR;
  165. +     }
  166. +     while (isdigit(*format)) {
  167. +         format++;
  168. +     }
  169. +     /* A precision that give the minimum numnber of digits to appear... */
  170. +     if (*format == '.') {
  171. +         format++;
  172. +     }
  173. +     if (*format == '*') {
  174. +         interp->result =
  175. +       "format string can't specify integer (*) precision argument";
  176. +     return TCL_ERROR;    /* Can't allow '*' in precision */
  177. +     }
  178. +     while (isdigit(*format)) {
  179. +         format++;
  180. +     }
  181. +     if (*format == 'l') {/* Ansi long specifier */
  182. +         format++;
  183. +     }
  184. +     switch (*format) {
  185. +     case 'D':
  186. +     case 'O':
  187. +     case 'U':
  188. +     case 'd':
  189. +     case 'o':
  190. +     case 'u':
  191. +     case 'x':
  192. +     case 'X':
  193. +     case 's':
  194. +     case 'c':
  195. +         interp->result = 
  196. +       "format string must specify floating point conversion";
  197. +     return TCL_ERROR;
  198. +     case 'e':
  199. +     case 'E':
  200. +     case 'f':
  201. +     case 'g':
  202. +     case 'G':
  203. +     break;
  204. +     case 0:
  205. +     interp->result = "format string ended in middle of field specifier";
  206. +     return TCL_ERROR;
  207. +     default:
  208. +     interp->result = "bad field specifier";
  209. +     return TCL_ERROR;
  210. +     }
  211. +     format++;
  212. +     if (*format != '\0') {
  213. +     interp->result = "found extra characters in format string";
  214. +     return TCL_ERROR;
  215. +     }
  216. +     return TCL_OK;
  217. + }
  218. + /*
  219. +  *----------------------------------------------------------------------
  220. +  *
  221. +  * OutputFormatProc --
  222. +  *
  223. +  *      This procedure is invoked when the global Tcl variable "OFMT"
  224. +  *    is written to or unset.  It sets the format conversion string
  225. +  *    which is used in tclExpr.c. The default format is "%g".
  226. +  *
  227. +  * Results:
  228. +  *      NULL is a valid output format string was given.
  229. +  *
  230. +  * Side effects:
  231. +  *      See the user documentation.
  232. +  *
  233. +  *----------------------------------------------------------------------
  234. +  */
  235. + /*ARGSUSED*/
  236. + char *
  237. + OutputFormatProc(clientData, interp, name1, name2, flags)
  238. +     ClientData clientData;   /* not used */
  239. +     Tcl_Interp *interp;
  240. +     char *name1;
  241. +     char *name2;         /* not used */
  242. +     int flags;
  243. + {
  244. +     if (flags & TCL_TRACE_UNSETS) {
  245. +     if (OFMT != defFmt)
  246. +         free (OFMT);
  247. +         OFMT = defFmt;
  248. +     if (flags & TCL_TRACE_DESTROYED) {     /* Restore the trace */
  249. +         Tcl_TraceVar2 (interp, name1, name2, 
  250. +                (TCL_TRACE_WRITES | TCL_TRACE_UNSETS), 
  251. +                OutputFormatProc, (ClientData) NULL);
  252. +     }
  253. +     } else if (flags & TCL_TRACE_WRITES) {
  254. +         char *newFmt;
  255. +     char *fmt;
  256. +     
  257. +     newFmt = Tcl_GetVar2(interp, name1, name2,
  258. +                 flags & (TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY));
  259. +     if (newFmt == NULL) {
  260. +         return (interp->result);
  261. +     }
  262. +     /* Test the conversion format string */
  263. +     if (VerifyFormat(interp, newFmt) != TCL_OK) {
  264. +         /* Restore variable to the previous valid value */
  265. +         Tcl_SetVar2 (interp, name1, name2, OFMT, flags & TCL_GLOBAL_ONLY);
  266. +         return (interp->result);
  267. +     }
  268. +     fmt = (char *)malloc(strlen(newFmt) + 1);
  269. +     if (fmt == NULL) {
  270. +         return "Can't allocate format string";
  271. +     }
  272. +     strcpy(fmt, newFmt);
  273. +     if (OFMT != defFmt)
  274. +         free (OFMT);
  275. +     OFMT = fmt;
  276. +     }
  277. +     return (NULL);
  278. + }
  279.  
  280.