source: trunk/lang/tcl/src/RpUnitsTclInterface.cc @ 1264

Last change on this file since 1264 was 1018, checked in by gah, 16 years ago

Massive changes: New directory/file layout

File size: 17.4 KB
Line 
1/*
2 * ----------------------------------------------------------------------
3 *  Rappture::units
4 *
5 *  This is an interface to the rappture units module.
6 *  It allows you to convert between units and format values.
7 * ======================================================================
8 *  AUTHOR:  Derrick Kearney, Purdue University
9 *  Copyright (c) 2004-2006  Purdue Research Foundation
10 *
11 *  See the file "license.terms" for information on usage and
12 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 * ======================================================================
14 */
15#include <tcl.h>
16#include "RpUnits.h"
17
18extern "C" Tcl_AppInitProc RpUnits_Init;
19
20static Tcl_CmdProc RpTclUnitsConvert;
21static Tcl_CmdProc RpTclUnitsDesc;
22static Tcl_CmdProc RpTclUnitsSysFor;
23static Tcl_CmdProc RpTclUnitsSysAll;
24static Tcl_CmdProc RpTclUnitsSearchFor;
25
26
27/**********************************************************************/
28// FUNCTION: RpUnits_Init()
29/// Initializes the Rappture Units module and commands defined below
30/**
31 * Called in Rappture_Init() to initialize the Rappture Units module.
32 * Initialized commands include:
33 * ::Rappture::Units::convert
34 * ::Rappture::Units::description
35 * ::Rappture::Units::System::for
36 * ::Rappture::Units::System::all
37 * ::Rappture::Units::Search::for
38 */
39
40extern "C" int
41RpUnits_Init(Tcl_Interp *interp)
42{
43
44    Tcl_CreateCommand(interp, "::Rappture::Units::convert",
45        RpTclUnitsConvert, (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
46
47    Tcl_CreateCommand(interp, "::Rappture::Units::description",
48        RpTclUnitsDesc, (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
49
50    Tcl_CreateCommand(interp, "::Rappture::Units::System::for",
51        RpTclUnitsSysFor, (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
52
53    Tcl_CreateCommand(interp, "::Rappture::Units::System::all",
54        RpTclUnitsSysAll, (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
55
56    Tcl_CreateCommand(interp, "::Rappture::Units::Search::for",
57        RpTclUnitsSearchFor, (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
58
59    return TCL_OK;
60}
61
62/**********************************************************************/
63// FUNCTION: RpTclUnitsConvert()
64/// Rappture::Units::convert function in Tcl, used to convert unit.
65/**
66 * Converts values between recognized units in Rappture.
67 * Full function call:
68 * ::Rappture::Units::convert <value> ?-context units? ?-to units? ?-units on/off?
69 *
70 * units attached to <value> take precedence over units
71 * provided in -context option.
72 */
73
74int
75RpTclUnitsConvert   (   ClientData cdata,
76                        Tcl_Interp *interp,
77                        int argc,
78                        const char *argv[]  )
79{
80    std::string inValue       = ""; // value provided by the user
81    std::string origUnitsName = "";
82    std::string fromUnitsName = ""; // name of the units for user's value
83    std::string toUnitsName   = ""; // name of the units to convert to
84    std::string option        = ""; // tmp var for parsing command line options
85    std::string val           = ""; // inValue + fromUnitsName as one string
86    std::string convertedVal  = ""; // result of conversion
87    int showUnits             = 1;  // flag if we should show units in result
88    int result                = 0;  // flag if the conversion was successful
89
90    int nextarg          = 1; // start parsing using the '2'th argument
91    int argsLeft         = 0; // temp variable for calculation
92    int retVal           = 0; // TCL_OK or TCL_ERROR depending on result val
93    char *endptr         = NULL;
94
95    int err                   = 0;  // err code for validate()
96    std::string type          = ""; // junk variable that validate() needs
97    std::list<std::string> compatList;
98    std::list<std::string>::iterator compatListIter;
99    std::string listStr       = ""; // string version of compatList
100    std::string mesg          = ""; // error mesg text
101
102    Tcl_ResetResult(interp);
103
104    // parse through command line options
105    if (argc < 2) {
106        Tcl_AppendResult(interp,
107            "wrong # args: should be \"",
108            // argv[0]," value args\"",
109            argv[0],
110            " <value> ?-context units? ?-to units? ?-units on/off?\"",
111            (char*)NULL);
112        return TCL_ERROR;
113    }
114
115    inValue = std::string(argv[nextarg++]);
116
117    argsLeft = (argc-nextarg);
118    while (argsLeft > 0 ) {
119        if (*argv[nextarg] == '-') {
120            option = std::string(argv[nextarg]);
121
122            if ( option == "-context" ) {
123                nextarg++;
124                if (argv[nextarg] != NULL) {
125                    fromUnitsName = std::string(argv[nextarg]);
126                    err = 0;
127                    origUnitsName = fromUnitsName;
128                    err = RpUnits::validate(fromUnitsName,type,&compatList);
129                    if ( err != 0) {
130                        Tcl_AppendResult(interp,
131                            "bad value \"", origUnitsName.c_str(),
132                            "\": should be a recognized unit for Rappture",
133                            (char*)NULL);
134                        return TCL_ERROR;
135                    }
136                }
137                else {
138                    // if user does not specify wishes for this option,
139                    // set fromUnitsName to an empty string.
140                    fromUnitsName = "";
141                }
142            }
143            else if ( option == "-to" ) {
144                nextarg++;
145                if (argv[nextarg] != NULL) {
146                    toUnitsName = std::string(argv[nextarg]);
147                    err = 0;
148                    origUnitsName = toUnitsName;
149                    err = RpUnits::validate(toUnitsName,type);
150                    if (err != 0) {
151                        Tcl_AppendResult(interp,
152                            "bad value \"", origUnitsName.c_str(),
153                            "\": should be a recognized unit for Rappture",
154                            (char*)NULL);
155                        return TCL_ERROR;
156                    }
157                }
158                else {
159                    // if user does not specify wishes for this option,
160                    // set toUnitsName to an empty string.
161                    toUnitsName = "";
162                }
163            }
164            else if ( option == "-units" ) {
165                nextarg++;
166                if (argv[nextarg] != NULL) {
167                    if (Tcl_GetBoolean(interp, argv[nextarg], &showUnits)) {
168                        // unrecognized value for -units option
169                        // Tcl_GetBoolean fills in error message
170                        // Tcl_AppendResult(interp,
171                        //     "expected boolean value but got \"",
172                        //     argv[nextarg], "\"", (char*)NULL);
173                        return TCL_ERROR;
174                    }
175                }
176                else {
177                    // if user does not specify wishes for this option,
178                    // return error.
179                    // unrecognized value for -units option
180                    Tcl_AppendResult(interp,
181                        "expected boolean value but got \"\"", (char*)NULL);
182                    return TCL_ERROR;
183                }
184            }
185            else {
186                // unrecognized option
187                Tcl_AppendResult(interp, "bad option \"", argv[nextarg],
188                        "\": should be -context, -to, -units",
189                        (char*)NULL);
190                return TCL_ERROR;
191            }
192
193            nextarg++;
194        }
195        else {
196            // unrecognized input
197            Tcl_AppendResult(interp, "bad option \"", argv[nextarg], "\": ",
198                "should be -context, -to, -units",
199                (char*)NULL);
200            return TCL_ERROR;
201
202        }
203
204        argsLeft = (argc-nextarg);
205    }
206
207    // check the inValue to see if it has units
208    // or if we should use those provided in -context option
209    strtod(inValue.c_str(),&endptr);
210    if (endptr == inValue.c_str()) {
211        // there was no numeric value that could be pulled from inValue
212        // return error
213
214        mesg =  "\": should be a real number with units";
215
216        if (!fromUnitsName.empty()) {
217            list2str(compatList,listStr);
218            mesg = mesg + " of (" + listStr + ")";
219        }
220 
221        Tcl_AppendResult(interp, "bad value \"",
222                inValue.c_str(), mesg.c_str(), (char*)NULL);
223        return TCL_ERROR;
224    }
225    else if ( ((unsigned)(endptr - inValue.c_str())) == inValue.length() ) {
226        // add 1 because we are subtracting indicies
227        // there were no units at the end of the inValue string
228        // rappture units convert expects the val variable to be
229        // the quantity and units in one string
230
231        if (!fromUnitsName.empty()) {
232            val = inValue + fromUnitsName;
233        }
234        else {
235            Tcl_AppendResult(interp, "value: \"", inValue.c_str(),
236                    "\" has unrecognized units", (char*)NULL);
237            return TCL_ERROR;
238        }
239    }
240    else {
241        // there seemed to be units at the end of the inValue string
242        // we will ignore the -context flag and use the units in inValue
243        val = inValue;
244    }
245
246    // call the rappture units convert function
247    convertedVal = RpUnits::convert(val, toUnitsName, showUnits, &result);
248
249    if ( (!convertedVal.empty()) && (result == 0) ) {
250        // store the new result in the interpreter
251        Tcl_AppendResult(interp, convertedVal.c_str(), (char*)NULL);
252        retVal = TCL_OK;
253    }
254    else {
255        // error while converting
256        Tcl_AppendResult(interp,
257                convertedVal.c_str(),
258                (char*)NULL);
259        retVal = TCL_ERROR;
260    }
261
262    return retVal;
263}
264
265/**********************************************************************/
266// FUNCTION: RpTclUnitsDesc()
267/// Rappture::Units::description function in Tcl, returns description of units
268/**
269 * Returns a description for the specified system of units.
270 * The description includes the abstract type (length, temperature, etc.)
271 * along with a list of all compatible systems.
272 *
273 * Full function call:
274 * ::Rappture::Units::description <units>
275 */
276
277int
278RpTclUnitsDesc      (   ClientData cdata,
279                        Tcl_Interp *interp,
280                        int argc,
281                        const char *argv[]  )
282{
283    std::string unitsName     = ""; // name of the units provided by user
284    std::string type          = ""; // name of the units provided by user
285    std::string listStr       = ""; // name of the units provided by user
286    // const RpUnits* unitsObj   = NULL;
287    std::list<std::string> compatList;
288
289    int nextarg               = 1; // start parsing using the '2'th argument
290    int err                   = 0; // err code for validate()
291
292    Tcl_ResetResult(interp);
293
294    // parse through command line options
295    if (argc != 2) {
296        Tcl_AppendResult(interp,
297                "wrong # args: should be \"", argv[0],
298                " units\"", (char*)NULL);
299        return TCL_ERROR;
300    }
301
302    unitsName = std::string(argv[nextarg]);
303
304    err = RpUnits::validate(unitsName,type,&compatList);
305    if (err) {
306        /*
307         * according to tcl version, in this case we
308         * should return an empty string. i happen to disagree.
309         * the next few lines is what i think the user should see.
310        Tcl_AppendResult(interp,
311            "bad value \"", unitsName.c_str(),
312            "\": should be a recognized unit for Rappture",
313            (char*)NULL);
314        return TCL_ERROR;
315        */
316        return TCL_OK;
317    }
318
319    Tcl_AppendResult(interp, type.c_str(), (char*)NULL);
320
321    list2str(compatList,listStr);
322
323    Tcl_AppendResult(interp, " (", listStr.c_str() ,")", (char*)NULL);
324
325    return TCL_OK;
326}
327
328/**********************************************************************/
329// FUNCTION: RpTclUnitsSysFor()
330/// Rappture::Units::System::for fxn in Tcl, returns system for given units
331/**
332 * Returns the system, as a string, for the given system of units, or ""
333 * if there is no system that matches the units string.
334 *
335 * Full function call:
336 * ::Rappture::Units::System::for <units>
337 */
338
339int
340RpTclUnitsSysFor    (   ClientData cdata,
341                        Tcl_Interp *interp,
342                        int argc,
343                        const char *argv[]  )
344{
345    std::string unitsName     = ""; // name of the units provided by user
346    std::string type          = ""; // type/system of units to be returned to user
347    int nextarg               = 1; // start parsing using the '2'th argument
348    int err                   = 0;
349
350    Tcl_ResetResult(interp);
351
352    // parse through command line options
353    if (argc != 2) {
354        Tcl_AppendResult(interp, "wrong # args: should be \"",
355                argv[0], " units\"", (char*)NULL);
356        return TCL_ERROR;
357    }
358
359    unitsName = std::string(argv[nextarg]);
360
361    // look in our dictionary of units to see if 'unitsName' is a valid unit
362    // if so, return its type (or system) in the variable 'type'.
363    err = RpUnits::validate(unitsName,type);
364    if (err) {
365        /*
366         * according to tcl version, in this case we
367         * should return an empty string. i happen to disagree.
368         * the next few lines is what i think the user should see.
369        Tcl_AppendResult(interp,
370            "The units named: \"", unitsName.c_str(),
371            "\" is not a recognized unit for rappture",
372            (char*)NULL);
373        return TCL_ERROR;
374        */
375        return TCL_OK;
376    }
377
378    Tcl_AppendResult(interp, type.c_str(), (char*)NULL);
379    return TCL_OK;
380
381}
382
383/**********************************************************************/
384// FUNCTION: RpTclUnitsSysAll()
385/// Rappture::Units::System::all fxn in Tcl, returns list of compatible units
386/**
387 * Returns a list of all units compatible with the given units string.
388 * Compatible units are determined by following all conversion
389 * relationships that lead to the same base system.
390 *
391 * Full function call:
392 * ::Rappture::Units::System::all <units>
393 */
394
395int
396RpTclUnitsSysAll    (   ClientData cdata,
397                        Tcl_Interp *interp,
398                        int argc,
399                        const char *argv[]  )
400{
401    std::string unitsName     = ""; // name of the units provided by user
402    std::string type          = ""; // junk variable that validate() needs
403    // const RpUnits* unitsObj   = NULL;
404    std::list<std::string> compatList;
405    std::list<std::string>::iterator compatListIter;
406    int nextarg               = 1; // start parsing using the '2'th argument
407    int err                   = 0; // err code for validate
408
409    Tcl_ResetResult(interp);
410
411    // parse through command line options
412    if (argc != 2) {
413        Tcl_AppendResult(interp, "wrong # args: should be \"",
414                argv[0], " units\"", (char*)NULL);
415        return TCL_ERROR;
416    }
417
418    unitsName = std::string(argv[nextarg]);
419
420    err = RpUnits::validate(unitsName,type,&compatList);
421    if (err) {
422        /*
423         * according to tcl version, in this case we
424         * should return an empty string. i happen to disagree.
425         * the next few lines is what i think the user should see.
426        Tcl_AppendResult(interp,
427            "The units named: \"", unitsName.c_str(),
428            "\" is not a recognized unit for rappture",
429            (char*)NULL);
430        return TCL_ERROR;
431        */
432        return TCL_OK;
433    }
434
435    compatListIter = compatList.begin();
436
437    while (compatListIter != compatList.end()) {
438        Tcl_AppendElement(interp,(*compatListIter).c_str());
439        // increment the iterator
440        compatListIter++;
441    }
442
443    return TCL_OK;
444}
445
446/**********************************************************************/
447// FUNCTION: RpTclUnitsSearchfor()
448/// Rappture::Units::Search::for fxn in Tcl, returns string of found units
449/**
450 * Returns a list of all units from the given units string that
451 * were found within the units dictionary. This function takes in a
452 * string with or without a value. The string at the very least should
453 * contain the units you are searching for in the dictionary. If the
454 * string contains a value as well, the value will be ignored. A value
455 * is considered any numeric sequence as defined by the function
456 * strtod().
457 *
458 * Full function call:
459 * ::Rappture::Units::Search::for <units>
460 */
461
462int
463RpTclUnitsSearchFor (  ClientData cdata,
464                        Tcl_Interp *interp,
465                        int argc,
466                        const char *argv[]  )
467{
468    std::string unitsName     = ""; // name of the units provided by user
469    std::string origUnitsName = ""; // name of the units provided by user
470    std::string type          = ""; // junk variable that validate() needs
471    int nextarg               = 1; // start parsing using the '2'th argument
472    int err                   = 0; // err code for validate
473    double val                = 0;
474
475    Tcl_ResetResult(interp);
476
477    // parse through command line options
478    if (argc != 2) {
479        Tcl_AppendResult(interp, "wrong # args: should be \"",
480                argv[0], " units\"", (char*)NULL);
481        return TCL_ERROR;
482    }
483
484    // find where the unitsName begins
485    unitSlice(std::string(argv[nextarg]),unitsName,val);
486
487    err = RpUnits::validate(unitsName,type);
488    if (err) {
489        /*
490         * according to tcl version, in this case we
491         * should return an empty string. i happen to disagree.
492         * the next few lines is what i think the user should see.
493        Tcl_AppendResult(interp,
494            "Unrecognized units: \"", origUnitsName.c_str(), "\"", (char*)NULL);
495        return TCL_ERROR;
496        */
497        return TCL_OK;
498    }
499
500    Tcl_AppendResult(interp, unitsName.c_str(), (char*)NULL);
501
502    return TCL_OK;
503}
Note: See TracBrowser for help on using the repository browser.