1 Guidelines for adding custom functions
2 --------------------------------------
4 Step 0: Determine if is should it be done?
6 The main focus for calc is to provide a portable platform for
7 multi-precision calculations in a C-like environment. You should
8 consider implementing algorithms in the calc language as a first
9 choice. Sometimes an algorithm requires use of special hardware, a
10 non-portable OS or pre-compiled C library. In these cases a custom
11 interface may be needed.
13 The custom function interface is intended to make is easy for
14 programmers to add functionality that would be otherwise
15 un-suitable for general distribution. Functions that are
16 non-portable (machine, hardware or OS dependent) or highly
17 specialized are possible candidates for custom functions.
19 So before you go to step 1, ask yourself:
21 + Can I implement this as a calc resource file or calc shell script?
23 If Yes, write the shell script or resource file and be done with it.
24 If No, continue to the next question ...
26 + Does it require the use of non-portable features,
27 OS specific support or special hardware?
29 If No, write it as a regular builtin function.
30 If Yes, continue to step 1 ...
33 Step 1: Do some background work
35 First ... read this file ALL THE WAY THROUGH before implementing
36 anything in Steps 2 and beyond!
38 If you are not familiar with calc internals, we recommend that
39 you look at some examples of custom functions. Look at the
40 the following source files:
47 help/custom (or run: calc help custom)
49 You would be well advised to look at a more recent calc source
50 such as one available in from the calc version archive.
51 See the following for more details:
53 help/archive (or run: calc help archive)
56 Step 2: Name your custom function
58 We suggest that you pick a name that does not conflict with
59 one of the builtin names. It makes it easier to get help
60 via the help interface and avoid confusion down the road.
62 You should avoid picking a name that matches a file or
63 directory name under ${HELPDIR} as well. Not all help
64 files are associated with builtin function names.
66 For purposes of this file, we will use the name 'curds'
67 as our example custom function name.
70 Step 3: Document your custom function
72 No this step is NOT out of order. We recommend that you write the
73 help file associated with your new custom function EARLY. By
74 experience we have found that the small amount of effort made to
75 write "how the custom function will be used" into a help file pays
76 off in a big way when it comes to coding. Often the effort of
77 writing a help file will clarify fuzzy aspects of your design.
78 Besides, unless you write the help file first, it will likely never
79 be written later on. :-(
81 OK ... we will stop preaching now ...
83 [[ From now on we will give filenames relative to the custom directory ]]
85 Take a look at one of the example custom help files:
92 You can save time by using one of the custom help files
93 as a template. Copy one of these files to your own help file:
98 and edit it accordingly.
101 Step 4: Write your test code
103 No this step is NOT out of order either. We recommend that you
104 write a simple calc script that will call your custom function and
107 This script will be useful while you are debugging your code. In
108 addition, if you wish to submit your code for distribution, this
109 test code will be an import part of your submission. Your test
110 code will also service as additional for your custom function.
112 Oops ... we said we would stop preaching, sorry about that ...
114 You can use one of the following as a template:
119 Copy one of these to your own file:
122 cp halflen.cal curds.cal
124 and exit it accordingly. In particular you will want to:
126 remove our header disclaimer (or put your own on)
128 change the name from halflen() to curds()
130 change the comment from 'halflen - determine the length ...' to
131 'curds - brief description about ...'
133 change other code as needed.
136 Step 5: Write your custom function
138 By convention, the files we ship that contain custom function
139 interface code in filenames of the form:
143 We suggest that you use filenames of the form:
147 to avoid filename conflicts.
149 We recommend that you use one of the c_*.c files as a template.
150 Copy an appropriate file to your file:
153 cp c_argv.c u_curds.c
155 Before you edit it, you should note that there are several important
156 features of this file.
158 a) All of the code in the file is found between #if ... #endif:
161 * only comments and blank lines at the top
166 ... all code, #includes, #defines etc.
170 This allows this code to 'go away' when the upper Makefile
171 disables the custom code (because ALLOW_CUSTOM no longer
172 has the -DCUSTOM define).
174 b) The function type must be:
178 u_curds(char *name, int count, VALUE **vals)
180 The 3 args are passed in by the custom interface
181 and have the following meaning:
183 name The name of the custom function that
184 was called. In particular, this is the first
185 string arg that was given to the custom()
186 builtin. This is the equivalent of argv[0] for
187 main() in C programming.
189 The same code can be used for multiple custom
190 functions by processing off of this value.
192 count This is the number of additional args that
193 was given to the custom() builtin. Note
194 that count does NOT include the name arg.
195 This is similar to argc except that count
196 is one less than the main() argc interface.
198 For example, a call of:
200 custom("curds", a, b, c)
202 would cause count to be passed as 3.
204 vals This is a pointer to an array of VALUEs.
205 This is the equivalent of argv+1 for
206 main() in C programming. The difference
207 here is that vals[0] refers to the 1st
208 parameter AFTER the same.
210 For example, a call of:
212 custom("curds", a, b, c)
214 would cause vals to point to the following array:
220 NOTE: If you do not use any of the 3 function parameters,
221 then you should declare that function parameter to be UNUSED.
222 For example, if the count and vals parameters were not used
223 in your custom function, then your declaraction should be:
227 u_curds(char *name, int UNUSED count, VALUE UNUSED **vals)
229 c) The return value is the function must be a VALUE.
231 The typical way to form a VALUE to return is by declaring
232 the following local variable:
234 VALUE result; /* what we will return */
236 d) You will need to include:
240 /* any #include <foobar.h> here */
242 #include "../have_const.h"
243 #include "../value.h"
246 #include "../have_unused.h"
248 Typically these will be included just below any system
249 includes and just below the #if defined(CUSTOM) line.
251 To better understand the VALUE type, read:
255 The VALUE is a union of major value types found inside calc.
256 The v_type VALUE element determines which union element is
257 being used. Assume that we have:
261 Then the value is determined according to v_type:
263 vp->v_type the value is which is a type defined in
264 ---------- ------------ ---------- ---------------
265 V_NULL (none) n/a n/a
266 V_INT vp->v_int long n/a
267 V_NUM vp->v_num NUMBER * ../qmath.h
268 V_COM vp->v_com COMPLEX * ../cmath.h
269 V_ADDR vp->v_addr VALUE * ../value.h
270 V_STR vp->v_str char * n/a
271 V_MAT vp->v_mat MATRIX * ../value.h
272 V_LIST vp->v_list LIST * ../value.h
273 V_ASSOC vp->v_assoc ASSOC * ../value.h
274 V_OBJ vp->v_obj OBJECT * ../value.h
275 V_FILE vp->v_file FILEID ../value.h
276 V_RAND vp->v_rand RAND * ../zrand.h
277 V_RANDOM vp->v_random RANDOM * ../zrandom.h
278 V_CONFIG vp->v_config CONFIG * ../config.h
279 V_HASH vp->v_hash HASH * ../hash.h
280 V_BLOCK vp->v_block BLOCK * ../block.h
282 The V_OCTET is under review and should not be used at this time.
284 There are a number of macros that may be used to determine
285 information about the numerical values (ZVALUE, NUMBER and COMPLEX).
286 you might also want to read the following to understand
287 some of the numerical types of ZVALUE, NUMBER and COMPLEX:
293 While we cannot go into full detail here are some cookbook
294 code for manipulating VALUEs. For these examples assume
295 that we will manipulate the return value:
297 VALUE result; /* what we will return */
301 result.v_type = V_NULL;
304 To return a long you need to convert it to a NUMBER:
308 result.v_type = V_NUM;
309 result.v_num = itoq(variable); /* see ../qmath.c */
312 To return a FULL you need to convert it to a NUMBER:
316 result.v_type = V_NUM;
317 result.v_num = utoq(variable); /* see ../qmath.c */
320 To convert a ZVALUE to a NUMBER*:
324 result.v_type = V_NUM;
325 result.v_num = qalloc(); /* see ../qmath.c */
326 result.v_num->num = variable;
329 To convert a small NUMBER* into a long:
334 variable = qtoi(num);
336 To obtain a ZVALUE from a NUMBER*, extract the numerator:
342 z_variable = num->num;
345 To be sure that the value will fit, use the ZVALUE test macros:
349 unsigned long u_variable;
351 short very_tiny_variable;
353 if (zgtmaxlong(z_num)) { /* see ../zmath.h */
354 variable = ztolong(z_num);
356 if (zgtmaxulong(z_num)) {
357 u_variable = ztoulong(z_num);
359 if (zgtmaxufull(z_num)) {
360 f_variable = ztofull(z_num);
362 if (zistiny(z_num)) {
363 very_tiny_variable = z1tol(z_num);
366 You can (and should) add debugging statements to your custom code
367 by examining bit 8 of the calc_debug config flag:
369 if (conf->calc_debug & CALCDBG_CUSTOM) {
370 fprintf(stderr, "%ssome custom debug note: msg\n",
371 (conf->tab_ok ? "\t" : ""),
372 ((msg == NULL) ? "((NULL))" : msg));
375 One is able to set bit 8 by way of the calc command line:
379 See the calc man page for details. One may also set that bit
380 while running calc by way of the config() builtin function:
382 config("calc_debug", 128);
384 See the help/config file for details on calc_debug.
386 Step 6: Register the function in the custom interface table
388 To allow the custom() builtin to transfer control to your function,
389 you need to add an entry into the CONST struct custom cust table
390 found in custom/custtbl.c:
393 * custom interface table
395 * The order of the elements in struct custom are:
397 * { "xyz", "brief description of the xyz custom function",
398 * minimum_args, maximum_args, c_xyz },
402 * minimum_args an int >= 0
403 * maximum_args an int >= minimum_args and <= MAX_CUSTOM_ARGS
405 * Use MAX_CUSTOM_ARGS for maximum_args is the maximum number of args
406 * is potentially 'unlimited'.
408 * If the brief description cannot fit on the same line as the name
409 * without wrapping on a 80 col window, the description is probably
410 * too long and will not look nice in the show custom output.
412 CONST struct custom cust[] = {
418 * add your own custom functions here
420 * We suggest that you sort the entries below by name
421 * so that show custom will produce a nice sorted list.
424 { "argv", "information about its args, returns arg count",
425 0, MAX_CUSTOM_ARGS, c_argv },
427 { "devnull", "does nothing",
428 0, MAX_CUSTOM_ARGS, c_devnull },
430 { "help", "help for custom functions",
433 { "sysinfo", "return a calc #define value",
440 * This must be at the end of this table!!!
446 The definition of struct custom may be found in custom.h.
448 It is important that your entry be placed inside the:
450 #if defined(CUSTOM) ... #endif /* CUSTOM */
452 lines so that when the custom interface is disabled by the upper
453 level Makefile, one does not have unsatisfied symbols.
455 The brief description should be brief so that 'show custom' looks well
456 formatted. If the brief description cannot fit on the same line as
457 the name without wrapping on a 80 col window, the description is
458 probably too long and will not look nice in the show custom output.
460 The minargs places a lower bound on the number of args that
461 must be supplied to the interface. This does NOT count
462 the name argument given to custom(). So if minargs is 2:
464 custom("curds") /* call blocked at high level interface */
465 custom("curds", a) /* call blocked at high level interface */
466 custom("curds", a, b) /* call passed down to "curds" interface */
468 The maxargs sets a limit on the number of args that may be passed.
469 If minargs == maxargs, then the call requires a fixed number of
470 argument. There is a upper limit on the number of args. If
471 one wants an effectively unlimited upper bound, use MAX_CUSTOM_ARGS.
473 Note that one must have:
475 0 <= minargs <= maxargs <= MAX_CUSTOM_ARGS
477 To allow the curds function to take at least 2 args and up
478 to 5 args, one would add the following entry to cust[]:
480 { "curds", "brief description about curds interface",
483 It is recommended that the cust[] remain in alphabetical order,
484 so one would place it before the "devnull" and after "argv".
486 Last, you must forward declare the u_curds near the top of the file:
492 * add your forward custom function declarations here
494 * Declare custom functions as follows:
496 * E_FUNC VALUE c_xyz(char*, int, VALUE**);
498 * We suggest that you sort the entries below by name.
500 E_FUNC VALUE c_argv(char*, int, VALUE**);
501 E_FUNC VALUE c_devnull(char*, int, VALUE**);
502 E_FUNC VALUE c_help(char*, int, VALUE**);
503 E_FUNC VALUE c_sysinfo(char*, int, VALUE**);
505 For u_curds we would add the line:
507 E_FUNC VALUE u_curds(char*, int, VALUE**);
510 Step 7: Add the required information to the custom/Makefile.head
512 The calc test script, curds.cal, should be added to the
513 CUSTOM_CALC_FILES Makefile variable found in custom/Makefile.head:
515 CUSTOM_CALC_FILES= argv.cal halflen.cal curds.cal
517 The help file, curds, should be added to the CUSTOM_HELP
518 custom/Makefile.head variable:
520 CUSTOM_HELP= argv devnull help sysinfo curds
522 If you needed to create any .h files to support u_curds.c, these
523 files should be added to the CUSTOM_H_SRC custom/Makefile.head variable:
525 CUSTOM_H_SRC= u_curds.h otherfile.h
527 Your u_curds.c file MUST be added to the CUSTOM_SRC custom/Makefile.head
530 CUSTOM_SRC= c_argv.c c_devnull.c c_help.c c_sysinfo.c u_curds.c
532 and so must the associated .o file:
534 CUSTOM_OBJ= c_argv.o c_devnull.o c_help.o c_sysinfo.o u_curds.o
537 Step 8: Compile and link in your code
539 If your calc was not previously setup to compile custom code,
540 you should set it up now. The upper level Makefile (and
541 the custom Makefile) should have the following Makefile
544 ALLOW_CUSTOM= -DCUSTOM
546 It is recommended that you build your code from the top level
547 Makefile. It saves having to sync the other Makefile values.
548 To try and build the new libcustcalc.a that contains u_curds.c:
550 (cd ..; make custom/libcustcalc.a)
552 Fix any compile and syntax errors as needed. :-)
554 Once libcustcalc.a successfully builds, compile calc:
559 And check to be sure that the regression test suite still
560 works without errors:
565 Step 9: Add the Make dependency tools
567 You should probably add the dependency lines to the bottom of
568 the Makefile. Given the required include files, you will at least
569 have the following entries placed at the bottom of the Makefile:
571 u_curds.o: ../alloc.h
572 u_curds.o: ../block.h
573 u_curds.o: ../byteswap.h
574 u_curds.o: ../calcerr.h
575 u_curds.o: ../cmath.h
576 u_curds.o: ../config.h
577 u_curds.o: ../endian_calc.h
579 u_curds.o: ../have_const.h
580 u_curds.o: ../have_malloc.h
581 u_curds.o: ../have_newstr.h
582 u_curds.o: ../have_stdlib.h
583 u_curds.o: ../have_string.h
584 u_curds.o: ../longbits.h
585 u_curds.o: ../nametype.h
586 u_curds.o: ../qmath.h
588 u_curds.o: ../value.h
589 u_curds.o: ../zmath.h
591 u_curds.o: ../custom.h
593 If you have the makedepend tool from the X11 development environment
594 (by Todd Brunhoff, Tektronix, Inc. and MIT Project Athena), you can
595 use the following to update your dependencies:
597 # cd to the top level calc directory if you are not there already
599 rm -f Makefile.bak custom/Makefile.bak
602 diff -c Makefile.bak Makefile # look at the changes
603 diff -c custom/Makefile.bak custom/Makefile # look at the changes
605 rm -f Makefile.bak custom/Makefile.bak # cleanup
609 Now that you have built calc with your new custom function, test it:
611 ./calc -C # run the new calc with the -C arg
613 And then try out our test suite:
615 C-style arbitrary precision calculator (version 2.10.3t5.1)
616 [Type "exit" to exit, or "help" for help.]
618 > read custom/curds.cal
619 curds(a, b, [c, d, e]) defined
621 > custom("curds", 2, 3, 4)
626 Once you are satisfied that everything works, install the new code:
628 # cd to the top level calc directory if you are not there already
632 Although calc does not run setuid, you may need to be root to install
633 the directories into which calc installs may be write protected.
638 Your custom function may be of interest to some people and/or
639 serve as an example of what one can do with custom functions.
643 help/contrib (or run: calc help contrib)
645 and consider submitting your custom function for possible
646 inclusion in later versions of calc.
648 ## Copyright (C) 1999-2007 Landon Curt Noll
650 ## Calc is open software; you can redistribute it and/or modify it under
651 ## the terms of the version 2.1 of the GNU Lesser General Public License
652 ## as published by the Free Software Foundation.
654 ## Calc is distributed in the hope that it will be useful, but WITHOUT
655 ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
656 ## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
657 ## Public License for more details.
659 ## A copy of version 2.1 of the GNU Lesser General Public License is
660 ## distributed with calc under the filename COPYING-LGPL. You should have
661 ## received a copy with calc; if not, write to Free Software Foundation, Inc.
662 ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
664 ## @(#) $Revision: 30.4 $
665 ## @(#) $Id: HOW_TO_ADD,v 30.4 2007/09/21 01:27:27 chongo Exp $
666 ## @(#) $Source: /usr/local/src/bin/calc/custom/RCS/HOW_TO_ADD,v $
668 ## Under source code control: 1997/03/10 03:03:21
669 ## File existed as early as: 1997
671 ## chongo <was here> /\oo/\ http://www.isthe.com/chongo/
672 ## Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/