source: branches/blt4/lang/tcl/src/RpUnitsTclInterface.cc @ 1897

Last change on this file since 1897 was 1326, checked in by gah, 16 years ago

Cast away warnings from usage of constant strings

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
210    double value;
211    value = strtod(inValue.c_str(),&endptr);
212    if (endptr == inValue.c_str()) {
213        // there was no numeric value that could be pulled from inValue
214        // return error
215
216        mesg =  "\": should be a real number with units";
217
218        if (!fromUnitsName.empty()) {
219            list2str(compatList,listStr);
220            mesg = mesg + " of (" + listStr + ")";
221        }
222 
223        Tcl_AppendResult(interp, "bad value \"",
224                inValue.c_str(), mesg.c_str(), (char*)NULL);
225        return TCL_ERROR;
226    }
227    else if ( ((unsigned)(endptr - inValue.c_str())) == inValue.length() ) {
228        // add 1 because we are subtracting indicies
229        // there were no units at the end of the inValue string
230        // rappture units convert expects the val variable to be
231        // the quantity and units in one string
232
233        if (!fromUnitsName.empty()) {
234            val = inValue + fromUnitsName;
235        }
236        else {
237            Tcl_AppendResult(interp, "value: \"", inValue.c_str(),
238                    "\" has unrecognized units", (char*)NULL);
239            return TCL_ERROR;
240        }
241    }
242    else {
243        // there seemed to be units at the end of the inValue string
244        // we will ignore the -context flag and use the units in inValue
245        val = inValue;
246    }
247
248    // call the rappture units convert function
249    convertedVal = RpUnits::convert(val, toUnitsName, showUnits, &result);
250
251    if ( (!convertedVal.empty()) && (result == 0) ) {
252        // store the new result in the interpreter
253        Tcl_AppendResult(interp, convertedVal.c_str(), (char*)NULL);
254        retVal = TCL_OK;
255    }
256    else {
257        // error while converting
258        Tcl_AppendResult(interp,
259                convertedVal.c_str(),
260                (char*)NULL);
261        retVal = TCL_ERROR;
262    }
263
264    return retVal;
265}
266
267/**********************************************************************/
268// FUNCTION: RpTclUnitsDesc()
269/// Rappture::Units::description function in Tcl, returns description of units
270/**
271 * Returns a description for the specified system of units.
272 * The description includes the abstract type (length, temperature, etc.)
273 * along with a list of all compatible systems.
274 *
275 * Full function call:
276 * ::Rappture::Units::description <units>
277 */
278
279int
280RpTclUnitsDesc      (   ClientData cdata,
281                        Tcl_Interp *interp,
282                        int argc,
283                        const char *argv[]  )
284{
285    std::string unitsName     = ""; // name of the units provided by user
286    std::string type          = ""; // name of the units provided by user
287    std::string listStr       = ""; // name of the units provided by user
288    // const RpUnits* unitsObj   = NULL;
289    std::list<std::string> compatList;
290
291    int nextarg               = 1; // start parsing using the '2'th argument
292    int err                   = 0; // err code for validate()
293
294    Tcl_ResetResult(interp);
295
296    // parse through command line options
297    if (argc != 2) {
298        Tcl_AppendResult(interp,
299                "wrong # args: should be \"", argv[0],
300                " units\"", (char*)NULL);
301        return TCL_ERROR;
302    }
303
304    unitsName = std::string(argv[nextarg]);
305
306    err = RpUnits::validate(unitsName,type,&compatList);
307    if (err) {
308        /*
309         * according to tcl version, in this case we
310         * should return an empty string. i happen to disagree.
311         * the next few lines is what i think the user should see.
312        Tcl_AppendResult(interp,
313            "bad value \"", unitsName.c_str(),
314            "\": should be a recognized unit for Rappture",
315            (char*)NULL);
316        return TCL_ERROR;
317        */
318        return TCL_OK;
319    }
320
321    Tcl_AppendResult(interp, type.c_str(), (char*)NULL);
322
323    list2str(compatList,listStr);
324
325    Tcl_AppendResult(interp, " (", listStr.c_str() ,")", (char*)NULL);
326
327    return TCL_OK;
328}
329
330/**********************************************************************/
331// FUNCTION: RpTclUnitsSysFor()
332/// Rappture::Units::System::for fxn in Tcl, returns system for given units
333/**
334 * Returns the system, as a string, for the given system of units, or ""
335 * if there is no system that matches the units string.
336 *
337 * Full function call:
338 * ::Rappture::Units::System::for <units>
339 */
340
341int
342RpTclUnitsSysFor    (   ClientData cdata,
343                        Tcl_Interp *interp,
344                        int argc,
345                        const char *argv[]  )
346{
347    std::string unitsName     = ""; // name of the units provided by user
348    std::string type          = ""; // type/system of units to be returned to user
349    int nextarg               = 1; // start parsing using the '2'th argument
350    int err                   = 0;
351
352    Tcl_ResetResult(interp);
353
354    // parse through command line options
355    if (argc != 2) {
356        Tcl_AppendResult(interp, "wrong # args: should be \"",
357                argv[0], " units\"", (char*)NULL);
358        return TCL_ERROR;
359    }
360
361    unitsName = std::string(argv[nextarg]);
362
363    // look in our dictionary of units to see if 'unitsName' is a valid unit
364    // if so, return its type (or system) in the variable 'type'.
365    err = RpUnits::validate(unitsName,type);
366    if (err) {
367        /*
368         * according to tcl version, in this case we
369         * should return an empty string. i happen to disagree.
370         * the next few lines is what i think the user should see.
371        Tcl_AppendResult(interp,
372            "The units named: \"", unitsName.c_str(),
373            "\" is not a recognized unit for rappture",
374            (char*)NULL);
375        return TCL_ERROR;
376        */
377        return TCL_OK;
378    }
379
380    Tcl_AppendResult(interp, type.c_str(), (char*)NULL);
381    return TCL_OK;
382
383}
384
385/**********************************************************************/
386// FUNCTION: RpTclUnitsSysAll()
387/// Rappture::Units::System::all fxn in Tcl, returns list of compatible units
388/**
389 * Returns a list of all units compatible with the given units string.
390 * Compatible units are determined by following all conversion
391 * relationships that lead to the same base system.
392 *
393 * Full function call:
394 * ::Rappture::Units::System::all <units>
395 */
396
397int
398RpTclUnitsSysAll    (   ClientData cdata,
399                        Tcl_Interp *interp,
400                        int argc,
401                        const char *argv[]  )
402{
403    std::string unitsName     = ""; // name of the units provided by user
404    std::string type          = ""; // junk variable that validate() needs
405    // const RpUnits* unitsObj   = NULL;
406    std::list<std::string> compatList;
407    std::list<std::string>::iterator compatListIter;
408    int nextarg               = 1; // start parsing using the '2'th argument
409    int err                   = 0; // err code for validate
410
411    Tcl_ResetResult(interp);
412
413    // parse through command line options
414    if (argc != 2) {
415        Tcl_AppendResult(interp, "wrong # args: should be \"",
416                argv[0], " units\"", (char*)NULL);
417        return TCL_ERROR;
418    }
419
420    unitsName = std::string(argv[nextarg]);
421
422    err = RpUnits::validate(unitsName,type,&compatList);
423    if (err) {
424        /*
425         * according to tcl version, in this case we
426         * should return an empty string. i happen to disagree.
427         * the next few lines is what i think the user should see.
428        Tcl_AppendResult(interp,
429            "The units named: \"", unitsName.c_str(),
430            "\" is not a recognized unit for rappture",
431            (char*)NULL);
432        return TCL_ERROR;
433        */
434        return TCL_OK;
435    }
436
437    compatListIter = compatList.begin();
438
439    while (compatListIter != compatList.end()) {
440        Tcl_AppendElement(interp,(*compatListIter).c_str());
441        // increment the iterator
442        compatListIter++;
443    }
444
445    return TCL_OK;
446}
447
448/**********************************************************************/
449// FUNCTION: RpTclUnitsSearchfor()
450/// Rappture::Units::Search::for fxn in Tcl, returns string of found units
451/**
452 * Returns a list of all units from the given units string that
453 * were found within the units dictionary. This function takes in a
454 * string with or without a value. The string at the very least should
455 * contain the units you are searching for in the dictionary. If the
456 * string contains a value as well, the value will be ignored. A value
457 * is considered any numeric sequence as defined by the function
458 * strtod().
459 *
460 * Full function call:
461 * ::Rappture::Units::Search::for <units>
462 */
463
464int
465RpTclUnitsSearchFor (  ClientData cdata,
466                        Tcl_Interp *interp,
467                        int argc,
468                        const char *argv[]  )
469{
470    std::string unitsName     = ""; // name of the units provided by user
471    std::string origUnitsName = ""; // name of the units provided by user
472    std::string type          = ""; // junk variable that validate() needs
473    int nextarg               = 1; // start parsing using the '2'th argument
474    int err                   = 0; // err code for validate
475    double val                = 0;
476
477    Tcl_ResetResult(interp);
478
479    // parse through command line options
480    if (argc != 2) {
481        Tcl_AppendResult(interp, "wrong # args: should be \"",
482                argv[0], " units\"", (char*)NULL);
483        return TCL_ERROR;
484    }
485
486    // find where the unitsName begins
487    unitSlice(std::string(argv[nextarg]),unitsName,val);
488
489    err = RpUnits::validate(unitsName,type);
490    if (err) {
491        /*
492         * according to tcl version, in this case we
493         * should return an empty string. i happen to disagree.
494         * the next few lines is what i think the user should see.
495        Tcl_AppendResult(interp,
496            "Unrecognized units: \"", origUnitsName.c_str(), "\"", (char*)NULL);
497        return TCL_ERROR;
498        */
499        return TCL_OK;
500    }
501
502    Tcl_AppendResult(interp, unitsName.c_str(), (char*)NULL);
503
504    return TCL_OK;
505}
Note: See TracBrowser for help on using the repository browser.