/usr/share/calc/help/new_custom is in apcalc-common 2.12.4.4-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 | Guidelines for adding custom functions
--------------------------------------
Step 0: Determine if is should it be done?
The main focus for calc is to provide a portable platform for
multi-precision calculations in a C-like environment. You should
consider implementing algorithms in the calc language as a first
choice. Sometimes an algorithm requires use of special hardware, a
non-portable OS or pre-compiled C library. In these cases a custom
interface may be needed.
The custom function interface is intended to make is easy for
programmers to add functionality that would be otherwise
un-suitable for general distribution. Functions that are
non-portable (machine, hardware or OS dependent) or highly
specialized are possible candidates for custom functions.
So before you go to step 1, ask yourself:
+ Can I implement this as a calc resource file or calc shell script?
If Yes, write the shell script or resource file and be done with it.
If No, continue to the next question ...
+ Does it require the use of non-portable features,
OS specific support or special hardware?
If No, write it as a regular builtin function.
If Yes, continue to step 1 ...
Step 1: Do some background work
First ... read this file ALL THE WAY THROUGH before implementing
anything in Steps 2 and beyond!
If you are not familiar with calc internals, we recommend that
you look at some examples of custom functions. Look at the
the following source files:
custom.c
custom.h
custom/custtbl.c
custom/c_*.[ch]
custom/*.cal
help/custom (or run: calc help custom)
You would be well advised to look at a more recent calc source
such as one available in from the calc version archive.
See the following for more details:
help/archive (or run: calc help archive)
Step 2: Name your custom function
We suggest that you pick a name that does not conflict with
one of the builtin names. It makes it easier to get help
via the help interface and avoid confusion down the road.
You should avoid picking a name that matches a file or
directory name under ${HELPDIR} as well. Not all help
files are associated with builtin function names.
For purposes of this file, we will use the name 'curds'
as our example custom function name.
Step 3: Document your custom function
No this step is NOT out of order. We recommend that you write the
help file associated with your new custom function EARLY. By
experience we have found that the small amount of effort made to
write "how the custom function will be used" into a help file pays
off in a big way when it comes to coding. Often the effort of
writing a help file will clarify fuzzy aspects of your design.
Besides, unless you write the help file first, it will likely never
be written later on. :-(
OK ... we will stop preaching now ...
[[ From now on we will give filenames relative to the custom directory ]]
Take a look at one of the example custom help files:
custom/devnull
custom/argv
custom/help
custom/sysinfo
You can save time by using one of the custom help files
as a template. Copy one of these files to your own help file:
cd custom
cp sysinfo curds
and edit it accordingly.
Step 4: Write your test code
No this step is NOT out of order either. We recommend that you
write a simple calc script that will call your custom function and
check the results.
This script will be useful while you are debugging your code. In
addition, if you wish to submit your code for distribution, this
test code will be an import part of your submission. Your test
code will also service as additional for your custom function.
Oops ... we said we would stop preaching, sorry about that ...
You can use one of the following as a template:
custom/argv.cal
custom/halflen.cal
Copy one of these to your own file:
cd custom
cp halflen.cal curds.cal
and exit it accordingly. In particular you will want to:
remove our header disclaimer (or put your own on)
change the name from halflen() to curds()
change the comment from 'halflen - determine the length ...' to
'curds - brief description about ...'
change other code as needed.
Step 5: Write your custom function
By convention, the files we ship that contain custom function
interface code in filenames of the form:
c_*.c
We suggest that you use filenames of the form:
u_*.c
to avoid filename conflicts.
We recommend that you use one of the c_*.c files as a template.
Copy an appropriate file to your file:
cd custom
cp c_argv.c u_curds.c
Before you edit it, you should note that there are several important
features of this file.
a) All of the code in the file is found between #if ... #endif:
/*
* only comments and blank lines at the top
*/
#if defined(CUSTOM)
... all code, #includes, #defines etc.
#endif /* CUSTOM */
This allows this code to 'go away' when the upper Makefile
disables the custom code (because ALLOW_CUSTOM no longer
has the -DCUSTOM define).
b) The function type must be:
/*ARGSUSED*/
VALUE
u_curds(char *name, int count, VALUE **vals)
The 3 args are passed in by the custom interface
and have the following meaning:
name The name of the custom function that
was called. In particular, this is the first
string arg that was given to the custom()
builtin. This is the equivalent of argv[0] for
main() in C programming.
The same code can be used for multiple custom
functions by processing off of this value.
count This is the number of additional args that
was given to the custom() builtin. Note
that count does NOT include the name arg.
This is similar to argc except that count
is one less than the main() argc interface.
For example, a call of:
custom("curds", a, b, c)
would cause count to be passed as 3.
vals This is a pointer to an array of VALUEs.
This is the equivalent of argv+1 for
main() in C programming. The difference
here is that vals[0] refers to the 1st
parameter AFTER the same.
For example, a call of:
custom("curds", a, b, c)
would cause vals to point to the following array:
vals[0] points to a
vals[1] points to b
vals[2] points to c
NOTE: If you do not use any of the 3 function parameters,
then you should declare that function parameter to be UNUSED.
For example, if the count and vals parameters were not used
in your custom function, then your declaraction should be:
/*ARGSUSED*/
VALUE
u_curds(char *name, int UNUSED count, VALUE UNUSED **vals)
c) The return value is the function must be a VALUE.
The typical way to form a VALUE to return is by declaring
the following local variable:
VALUE result; /* what we will return */
d) You will need to include:
#if defined(CUSTOM)
/* any #include <foobar.h> here */
#include "../have_const.h"
#include "../value.h"
#include "custom.h"
#include "../have_unused.h"
Typically these will be included just below any system
includes and just below the #if defined(CUSTOM) line.
To better understand the VALUE type, read:
../value.h
The VALUE is a union of major value types found inside calc.
The v_type VALUE element determines which union element is
being used. Assume that we have:
VALUE *vp;
Then the value is determined according to v_type:
vp->v_type the value is which is a type defined in
---------- ------------ ---------- ---------------
V_NULL (none) n/a n/a
V_INT vp->v_int long n/a
V_NUM vp->v_num NUMBER * ../qmath.h
V_COM vp->v_com COMPLEX * ../cmath.h
V_ADDR vp->v_addr VALUE * ../value.h
V_STR vp->v_str char * n/a
V_MAT vp->v_mat MATRIX * ../value.h
V_LIST vp->v_list LIST * ../value.h
V_ASSOC vp->v_assoc ASSOC * ../value.h
V_OBJ vp->v_obj OBJECT * ../value.h
V_FILE vp->v_file FILEID ../value.h
V_RAND vp->v_rand RAND * ../zrand.h
V_RANDOM vp->v_random RANDOM * ../zrandom.h
V_CONFIG vp->v_config CONFIG * ../config.h
V_HASH vp->v_hash HASH * ../hash.h
V_BLOCK vp->v_block BLOCK * ../block.h
The V_OCTET is under review and should not be used at this time.
There are a number of macros that may be used to determine
information about the numerical values (ZVALUE, NUMBER and COMPLEX).
you might also want to read the following to understand
some of the numerical types of ZVALUE, NUMBER and COMPLEX:
../zmath.h
../qmath.h
../cmath.h
While we cannot go into full detail here are some cookbook
code for manipulating VALUEs. For these examples assume
that we will manipulate the return value:
VALUE result; /* what we will return */
To return NULL:
result.v_type = V_NULL;
return result;
To return a long you need to convert it to a NUMBER:
long variable;
result.v_type = V_NUM;
result.v_num = itoq(variable); /* see ../qmath.c */
return result;
To return a FULL you need to convert it to a NUMBER:
FULL variable;
result.v_type = V_NUM;
result.v_num = utoq(variable); /* see ../qmath.c */
return result;
To convert a ZVALUE to a NUMBER*:
ZVALUE variable;
result.v_type = V_NUM;
result.v_num = qalloc(); /* see ../qmath.c */
result.v_num->num = variable;
return result;
To convert a small NUMBER* into a long:
NUMBER *num;
long variable;
variable = qtoi(num);
To obtain a ZVALUE from a NUMBER*, extract the numerator:
NUMBER *num;
ZVALUE z_variable;
if (qisint(num)) {
z_variable = num->num;
}
To be sure that the value will fit, use the ZVALUE test macros:
ZVALUE z_num;
long variable;
unsigned long u_variable;
FULL f_variable;
short very_tiny_variable;
if (zgtmaxlong(z_num)) { /* see ../zmath.h */
variable = ztolong(z_num);
}
if (zgtmaxulong(z_num)) {
u_variable = ztoulong(z_num);
}
if (zgtmaxufull(z_num)) {
f_variable = ztofull(z_num);
}
if (zistiny(z_num)) {
very_tiny_variable = z1tol(z_num);
}
You can (and should) add debugging statements to your custom code
by examining bit 8 of the calc_debug config flag:
if (conf->calc_debug & CALCDBG_CUSTOM) {
fprintf(stderr, "%ssome custom debug note: msg\n",
(conf->tab_ok ? "\t" : ""),
((msg == NULL) ? "((NULL))" : msg));
}
One is able to set bit 8 by way of the calc command line:
calc -D 128
See the calc man page for details. One may also set that bit
while running calc by way of the config() builtin function:
config("calc_debug", 128);
See the help/config file for details on calc_debug.
Step 6: Register the function in the custom interface table
To allow the custom() builtin to transfer control to your function,
you need to add an entry into the CONST struct custom cust table
found in custom/custtbl.c:
/*
* custom interface table
*
* The order of the elements in struct custom are:
*
* { "xyz", "brief description of the xyz custom function",
* minimum_args, maximum_args, c_xyz },
*
* where:
*
* minimum_args an int >= 0
* maximum_args an int >= minimum_args and <= MAX_CUSTOM_ARGS
*
* Use MAX_CUSTOM_ARGS for maximum_args is the maximum number of args
* is potentially 'unlimited'.
*
* If the brief description cannot fit on the same line as the name
* without wrapping on a 80 col window, the description is probably
* too long and will not look nice in the show custom output.
*/
CONST struct custom cust[] = {
#if defined(CUSTOM)
/*
* add your own custom functions here
*
* We suggest that you sort the entries below by name
* so that show custom will produce a nice sorted list.
*/
{ "argv", "information about its args, returns arg count",
0, MAX_CUSTOM_ARGS, c_argv },
{ "devnull", "does nothing",
0, MAX_CUSTOM_ARGS, c_devnull },
{ "help", "help for custom functions",
1, 1, c_help },
{ "sysinfo", "return a calc #define value",
0, 1, c_sysinfo },
#endif /* CUSTOM */
/*
* This must be at the end of this table!!!
*/
{NULL, NULL,
0, 0, NULL}
};
The definition of struct custom may be found in custom.h.
It is important that your entry be placed inside the:
#if defined(CUSTOM) ... #endif /* CUSTOM */
lines so that when the custom interface is disabled by the upper
level Makefile, one does not have unsatisfied symbols.
The brief description should be brief so that 'show custom' looks well
formatted. If the brief description cannot fit on the same line as
the name without wrapping on a 80 col window, the description is
probably too long and will not look nice in the show custom output.
The minargs places a lower bound on the number of args that
must be supplied to the interface. This does NOT count
the name argument given to custom(). So if minargs is 2:
custom("curds") /* call blocked at high level interface */
custom("curds", a) /* call blocked at high level interface */
custom("curds", a, b) /* call passed down to "curds" interface */
The maxargs sets a limit on the number of args that may be passed.
If minargs == maxargs, then the call requires a fixed number of
argument. There is a upper limit on the number of args. If
one wants an effectively unlimited upper bound, use MAX_CUSTOM_ARGS.
Note that one must have:
0 <= minargs <= maxargs <= MAX_CUSTOM_ARGS
To allow the curds function to take at least 2 args and up
to 5 args, one would add the following entry to cust[]:
{ "curds", "brief description about curds interface",
2, 5, u_curds },
It is recommended that the cust[] remain in alphabetical order,
so one would place it before the "devnull" and after "argv".
Last, you must forward declare the u_curds near the top of the file:
#if defined(CUSTOM)
/*
* add your forward custom function declarations here
*
* Declare custom functions as follows:
*
* E_FUNC VALUE c_xyz(char*, int, VALUE**);
*
* We suggest that you sort the entries below by name.
*/
E_FUNC VALUE c_argv(char*, int, VALUE**);
E_FUNC VALUE c_devnull(char*, int, VALUE**);
E_FUNC VALUE c_help(char*, int, VALUE**);
E_FUNC VALUE c_sysinfo(char*, int, VALUE**);
For u_curds we would add the line:
E_FUNC VALUE u_curds(char*, int, VALUE**);
Step 7: Add the required information to the custom/Makefile.head
The calc test script, curds.cal, should be added to the
CUSTOM_CALC_FILES Makefile variable found in custom/Makefile.head:
CUSTOM_CALC_FILES= argv.cal halflen.cal curds.cal
The help file, curds, should be added to the CUSTOM_HELP
custom/Makefile.head variable:
CUSTOM_HELP= argv devnull help sysinfo curds
If you needed to create any .h files to support u_curds.c, these
files should be added to the CUSTOM_H_SRC custom/Makefile.head variable:
CUSTOM_H_SRC= u_curds.h otherfile.h
Your u_curds.c file MUST be added to the CUSTOM_SRC custom/Makefile.head
variable:
CUSTOM_SRC= c_argv.c c_devnull.c c_help.c c_sysinfo.c u_curds.c
and so must the associated .o file:
CUSTOM_OBJ= c_argv.o c_devnull.o c_help.o c_sysinfo.o u_curds.o
Step 8: Compile and link in your code
If your calc was not previously setup to compile custom code,
you should set it up now. The upper level Makefile (and
the custom Makefile) should have the following Makefile
variable defined:
ALLOW_CUSTOM= -DCUSTOM
It is recommended that you build your code from the top level
Makefile. It saves having to sync the other Makefile values.
To try and build the new libcustcalc.a that contains u_curds.c:
(cd ..; make custom/libcustcalc.a)
Fix any compile and syntax errors as needed. :-)
Once libcustcalc.a successfully builds, compile calc:
cd ..
make calc
And check to be sure that the regression test suite still
works without errors:
make check
Step 9: Add the Make dependency tools
You should probably add the dependency lines to the bottom of
the Makefile. Given the required include files, you will at least
have the following entries placed at the bottom of the Makefile:
u_curds.o: ../alloc.h
u_curds.o: ../block.h
u_curds.o: ../byteswap.h
u_curds.o: ../calcerr.h
u_curds.o: ../cmath.h
u_curds.o: ../config.h
u_curds.o: ../endian_calc.h
u_curds.o: ../hash.h
u_curds.o: ../have_const.h
u_curds.o: ../have_malloc.h
u_curds.o: ../have_newstr.h
u_curds.o: ../have_stdlib.h
u_curds.o: ../have_string.h
u_curds.o: ../longbits.h
u_curds.o: ../nametype.h
u_curds.o: ../qmath.h
u_curds.o: ../shs.h
u_curds.o: ../value.h
u_curds.o: ../zmath.h
u_curds.o: u_curds.c
u_curds.o: ../custom.h
If you have the makedepend tool from the X11 development environment
(by Todd Brunhoff, Tektronix, Inc. and MIT Project Athena), you can
use the following to update your dependencies:
# cd to the top level calc directory if you are not there already
rm -f Makefile.bak custom/Makefile.bak
make depend
diff -c Makefile.bak Makefile # look at the changes
diff -c custom/Makefile.bak custom/Makefile # look at the changes
rm -f Makefile.bak custom/Makefile.bak # cleanup
Step 10: Test
Now that you have built calc with your new custom function, test it:
./calc -C # run the new calc with the -C arg
And then try out our test suite:
C-style arbitrary precision calculator (version 2.10.3t5.1)
[Type "exit" to exit, or "help" for help.]
> read custom/curds.cal
curds(a, b, [c, d, e]) defined
> custom("curds", 2, 3, 4)
Step 11: Install
Once you are satisfied that everything works, install the new code:
# cd to the top level calc directory if you are not there already
make install
Although calc does not run setuid, you may need to be root to install
the directories into which calc installs may be write protected.
Step 12: Contribute
Your custom function may be of interest to some people and/or
serve as an example of what one can do with custom functions.
Read the file:
help/contrib (or run: calc help contrib)
and consider submitting your custom function for possible
inclusion in later versions of calc.
## Copyright (C) 1999-2007 Landon Curt Noll
##
## Calc is open software; you can redistribute it and/or modify it under
## the terms of the version 2.1 of the GNU Lesser General Public License
## as published by the Free Software Foundation.
##
## Calc is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
## Public License for more details.
##
## A copy of version 2.1 of the GNU Lesser General Public License is
## distributed with calc under the filename COPYING-LGPL. You should have
## received a copy with calc; if not, write to Free Software Foundation, Inc.
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
##
## @(#) $Revision: 30.4 $
## @(#) $Id: HOW_TO_ADD,v 30.4 2007/09/21 01:27:27 chongo Exp $
## @(#) $Source: /usr/local/src/cmd/calc/custom/RCS/HOW_TO_ADD,v $
##
## Under source code control: 1997/03/10 03:03:21
## File existed as early as: 1997
##
## chongo <was here> /\oo/\ http://www.isthe.com/chongo/
## Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
|