[comp.sources.amiga] v89i209: graph - plot mathematical functions, Part06/07

page%swap@Sun.COM (Bob Page) (11/13/89)

Submitted-by: dg3i+@andrew.cmu.edu (David Gay)
Posting-number: Volume 89, Issue 209
Archive-name: applications/graph.6

# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
#	object.guidelines
#	object.h
#	pos.c
#	r_of_t.c
#	r_t.c
#	smallsym.c
#	tracker.c
#	tracker.h
#	tracker.readme
# This is archive 6 of a 7-part kit.
# This archive created: Sun Nov 12 18:23:31 1989
echo "extracting object.guidelines"
sed 's/^X//' << \SHAR_EOF > object.guidelines
XThere are actually two types of objects (should I have two main object
Xclasses, inherited from a common root ?):
X
Xa) Objects which are visual (labels, lines, rectangles)
Xb) Objects which exist semi-independtly from the graph (functions)
X
XObjects of class b are selectable even if the graph is invalid, they have a
Xname. Hence, as they can be selected other than by clicking on them, they are
Xallowed to be 'invalid' (not displayable). All this is not allowed for
Xclass a). The name and ok fields serve these purposes, name must be a null
Xstring and ok true for class a).
X
XThe methods will be called as expected: all created objects will be deleted,
Xselects will be followed by deselects, the sequence for down/move/up is
X
Xdown, move, move, ..., up
X
Xselect will be called if down returns true (the actual sequence is therefore:
X- if down returns FALSE   down
X- if down returns TRUE    down, select, move, move, ..., up
X)
X
XThe graph will be in a valid state (ok, window coords exisiting) when
Xdown/move/up/draw are called, but not necessarily so for all the other calls.
X
X
XPublic fields:
X--------------
X
Xnode node: used to keep objects in a list
Xstruct graph *g: the graph in which this object is
Xint ok: object in a valid state ?
Xint mx, my: These specify an offset to apply to the position of the mouse
Xwhile it is moved around. This allows you to have an object picked up and
Xmoved around by an arbitray point (the mouse will maintain the same position
Xrelative to the objects origin thoughout, eg when dragging an object).
X
Xmethods:
X--------
X
Xdelete      no comment
X
Xselect      you have been selected. You should probbaly highlight yourself.
X
Xdeselect    Deselect yourself (dehighlight ?), return TRUE if the graph
X            needs redrawing (eg if you can't unhighlight cleanly).
X
Xdown        Return true if graph's coordinates (in g->s.x,y) are inside you.
X            If you answer TRUE, you will be selected (cf select).
X
Xmove        The mouse has moved. Do what you want (resize, drag, nothing).
X            Called only if you answered TRUE to down (Coordinates as above).
X
Xup          The mouse has been released. return TRUE if the graph needs
X            redrawing (eg if you moved).
X
Xedit        Allow the user detailed control over your placement, ... Return
X            TRUE if the object changed.
X
Xdraw        Display thyself ! allow_mes is FALSE if called during refresh
X            (this mainly means that you can't display error messages).
X
Ximprove     Improve your appearance (for functions mainly). Return TRUE if
X            graph needs redrawing.
X
Xf2str       Provide a concise (one line) textual description of yourself, in
X            buf (maximum size: maxlen). Return buf.
X
Xvar_change  Is called for every object whenever a variable changes value. The
X            variable that has changed is passed. You should do whatever is
X            necessary to make sure that the next time you are displayed, you
X            reflect the new value of the variable.
X
Xsave        Save yourself in the file. For the format, see load_<obj> below.
X
Xinform      Called for every object of a graph when it changes (for the moment,
X     
X            only when the dots per meter values change). This allows any
X            dependent values to be recalculated, any fonts to be loaded, etc
X            If this isn't possible (eg no memory left), return FALSE and
X            return to your previous state, otherwise return TRUE (but keep
X            enough information to return to your previous state without
X            needing any resources).
X
Xconfirm     After an inform (to which you retuned TRUE), confirm is called
X            (before anything else), with ok being TRUE to confirm the
X            changes to the graph (you can now free any old resources), or
X            FALSE if the changes failed (return to the old state and free
X            any newly allocated resources).
X            Note: if you answered FALSE.to inform, this method won't get
X            called.
X
Xfunctions
X---------
XYou must also provide two functions to create the objects:
X
Xnew_<objname>   Create a new instance of an object of class objname. Provide
X                whatever initial parameters are necessary, or prompt the user.
X                The main code will obviously have to be modified to allow
X                creation of ant new object type ...
X
Xload_<objname>  Also creates a new instance, but with ant information loaded
X                from disk (The file is one of the parameters). The format, in
X                the file must be:
X
X                <object tag> a unique short for each type of object
X                <any private data>
X                <object end tag> another unique short
X
X                When load is called, the first tag has been read. load should
X                check for data integrity, and the presence of the end_tag.
X                NULL should be returned as an error.
X
X                The tags are centralised in file.h, to avoid errors.
X
SHAR_EOF
echo "extracting object.h"
sed 's/^X//' << \SHAR_EOF > object.h
X/*
X *                 GRAPH, Version 1.00 - 4 August 1989
X *
X *            Copyright 1989, David Gay. All Rights Reserved.
X *            This software is freely redistrubatable.
X */
X
X/* The object class. All the different objects are subclasses of it */
X#ifndef OBJECT_H
X#define OBJECT_H
X
X#include <stdio.h>
X#include "list.h"
X#include "grph.h"
X
X#define FNAMELEN 20 /* Length of object name */
X
Xstruct object
X{
X    node node;
X    struct graph *g; /* Which graph this object is in (g->s.x,y contains curren
Xt mouse pos)*/
X    char name [FNAMELEN]; /* Name (for functions only) "" otherwise */
X    int ok; /* For functions only: is it displayed */
X    int mx, my; /* Specify an (optional) displacement for the mouse pointer whe
Xn this is selected */
X    struct Region *(*delete)    (struct object *this);
X    void           (*select)    (struct object *this); /* You have been selecte
Xd */
X    struct Region *(*deselect)  (struct object *this); /* done. does the screen
X need refreshing  ? */
X    int            (*down)      (struct object *this); /* User pressed button.
XReturn TRUE if (x,y) is inside you */
X    void           (*move)      (struct object *this);
X    struct Region *(*up)        (struct object *this); /* Redraw necessary ? */
X     
X    int            (*edit)      (struct object *this, struct Region **ref); /*
XReturns true if graph changed */
X    void           (*draw)      (struct object *this, int allow_mes);
X    struct Region *(*improve)   (struct object *this);
X    char          *(*f2str)     (struct object *this, char *buf, int maxlen);
X    void           (*var_change)(struct object *this, char *name); /* Change in
X variable "name" */
X    int            (*save)      (struct object *this, FILE *f);
X    int            (*inform)    (struct object *this); /* Inform of changes to
Xg */
X    void           (*confirm)   (struct object *this, int ok); /* Confirm previ
Xous changes */
X};
X
X/* A special object, used for selecting points on screen (cross hair mode) */
X/* The fields are public. Only new, down, move, up and deselect are implemented
X  */
Xstruct pos
X{
X    struct object o;
X    int cross : 1;
X    int rect : 1;
X    double x0, y0, x1, y1;
X};
X
Xstruct pos *new_pos(struct graph *g);
X
X/* A label on the graph */
Xstruct label *new_label(struct graph *g, double x, double y);
Xstruct label *load_label(struct graph *g, FILE *f);
X
X/* A function (ie f(x), r(theta), etc */
Xstruct function *new_function(struct graph *g);
Xstruct function *load_function(struct graph *g, FILE *f);
X
X#endif
X
SHAR_EOF
echo "extracting pos.c"
sed 's/^X//' << \SHAR_EOF > pos.c
X/*
X *                 GRAPH, Version 1.00 - 4 August 1989
X *
X *            Copyright 1989, David Gay. All Rights Reserved.
X *            This software is freely redistrubatable.
X */
X
X#include <exec/types.h>
X#include <graphics/text.h>
X#include <math.h>
X
X#include "object.h"
X#include "object/default.h"
X#include "uio.h"
X#include "grph.h"
X#include "coords.h"
X#include "tracker.h"
X
X#include <proto/exec.h>
X#include <proto/graphics.h>
X
X/*-------------------------------------------------------------------------*/
X/*                        pos class implementation                         */
X/*-------------------------------------------------------------------------*/
X
X/* Size of cross */
X#define XCROSS 3
X#define YCROSS 3
X
X/* Redraw cross/rect */
Xstatic void draw_pos(struct pos *this, int allow_mes)
X{
X    struct graph *g = this->o.g;
X    struct RastPort *rp = g->io.rw->rp;
X
X    SetDrMd(rp, COMPLEMENT);
X    if (this->cross) /* Draw cross */
X    {
X        RMove(g->io.rw, this->x0, this->y0);
X        Move(rp, rp->cp_x - XCROSS, rp->cp_y);
X        Draw(rp, rp->cp_x + 2 * XCROSS, rp->cp_y);
X        Move(rp, rp->cp_x - XCROSS, rp->cp_y - YCROSS);
X        Draw(rp, rp->cp_x, rp->cp_y + 2 * YCROSS);
X    }
X    if (this->rect) /* Draw rectangle */
X    {
X        long const sx0 = ftol(g->io.rw->sx(g->io.rw, this->x0));
X        long const sx1 = ftol(g->io.rw->sx(g->io.rw, this->x1));
X        long const sy0 = ftol(g->io.rw->sy(g->io.rw, this->y0));
X        long const sy1 = ftol(g->io.rw->sy(g->io.rw, this->y1));
X        long c1 = ReadPixel(rp, sx0, sy0);
X        long c2 = ReadPixel(rp, sx0, sy1);
X        long c3 = ReadPixel(rp, sx1, sy0);
X        long c4 = ReadPixel(rp, sx1, sy1);
X
X        Move(rp, sx0, sy0);
X        Draw(rp, sx1, sy0);
X        Draw(rp, sx1, sy1);
X        Draw(rp, sx0, sy1);
X        Draw(rp, sx0, sy0);
X        /* Had problems with the corners ... */
X        if (c1 == ReadPixel(rp, sx0, sy0)) WritePixel(rp, sx0, sy0);
X        if (c2 == ReadPixel(rp, sx0, sy1)) WritePixel(rp, sx0, sy1);
X        if (c3 == ReadPixel(rp, sx1, sy0)) WritePixel(rp, sx1, sy0);
X        if (c4 == ReadPixel(rp, sx1, sy1)) WritePixel(rp, sx1, sy1);
X    }
X}
X
X/* The object exists only to be "selected" --> */
X/* This actually deletes the object !!! */
Xstatic struct Region *deselect_pos(struct pos *this)
X{
X    if (this->o.g->ok && this->o.g->io.rw) draw_pos(this, TRUE); /* erase */
X    FreeMem(this, sizeof(struct pos));
X    return NULL;
X}
X
Xstatic int down_pos(struct pos *this)
X{
X    return FALSE; /* You can never select a 'pos' */
X}
X
X/* Move, ie define rectangle */
Xstatic void move_pos(struct pos *this)
X{
X    draw_pos(this, TRUE);   /* erase old */
X    this->x1 = this->o.g->s.x;
X    this->y1 = this->o.g->s.y;
X    this->cross = FALSE; this->rect = TRUE; /* Not a cross any more */
X    draw_pos(this, TRUE);   /* draw new */
X}
X
X/* Rectangle is now defined */
Xstatic struct Region *up_pos(struct pos *this)
X{
X    draw_pos(this, TRUE);
X    this->x1 = this->o.g->s.x;
X    this->y1 = this->o.g->s.y;
X    draw_pos(this, TRUE);
X
X    return NULL;
X}
X
X/* Create a rectangle/cross (starts off as a cross) */
Xstruct pos *new_pos(struct graph *g)
X{
X    struct pos *this;
X    const static struct pos def_p = {
X        {
X            { NULL },
X            NULL, "", TRUE, 0, 0,
X            (void *)uncalled, (void *)uncalled, (void *)deselect_pos, (void *)d
Xown_pos, (void *)move_pos, (void *)up_pos, (void *)uncalled, (void *)draw_pos, (
Xvoid *)uncalled, (void *)uncalled, (void *)uncalled, (void *)uncalled, (void *)u
Xncalled,
X            (void *)uncalled
X        },
X        TRUE, FALSE
X    };
X
X    if (g->ok && g->io.rw)
X        if (this = AllocMem(sizeof(struct pos), 0L))
X        {
X            *this = def_p;
X            this->o.g = g;
X            this->x0 = this->x1 = g->s.x;
X            this->y0 = this->y1 = g->s.y;
X            draw_pos(this, TRUE); /* draw it */
X
X            return this;
X        }
X        else message(g, "No memory !", (char *)NULL);
X    else message(g, "No scale set", (char *)NULL);
X    return NULL;
X}
X
SHAR_EOF
echo "extracting r_of_t.c"
sed 's/^X//' << \SHAR_EOF > r_of_t.c
X/*
X *                 GRAPH, Version 1.00 - 4 August 1989
X *
X *            Copyright 1989, David Gay. All Rights Reserved.
X *            This software is freely redistrubatable.
X */
X
X#include <exec/types.h>
X#include <intuition/intuition.h>
X#include <graphics/text.h>
X#include <math.h>
X#include <string.h>
X
X#include "object.h"
X#include "object/function.h"
X#include "file.h"
X#include "graph.h"
X#include "uio.h"
X#include "coords.h"
X#include "list.h"
X#include "grph.h"
X#include "user/eval.h"
X#include "user/gadgets.h"
X#include "tracker.h"
X
X#include <proto/exec.h>
X#include <proto/intuition.h>
X#include <proto/graphics.h>
X
X/* (private) class r_of_t, inherits from function */
Xstruct r_of_t {
X    struct function f;
X    char expr[EXPRLEN];
X    value function, derivee;
X};
X
X/* (private) class, inherits from point */
Xstruct point_r_of_t
X{
X    point p;
X    double r, theta;
X};
X
Xtypedef struct point_r_of_t point_r_of_t;
X
X/*-------------------------------------------------------------------------*/
X/*                      r_of_t class implementation                        */
X/*-------------------------------------------------------------------------*/
X
X/* Is the function displayable ? */
Xstatic int r_of_t_ok(const struct r_of_t *this)
X{
X    return this->f.min != NOVAL && this->f.max != NOVAL &&
X           this->f.min < this->f.max &&
X           (this->f.steps == INOVAL || this->f.steps >= 3);
X}
X
X/* Free resources */
Xstatic void destroy_r_of_t(struct r_of_t *this)
X{
X    free_var_list(&this->f.used);
X    if (this->f.calc) free_list(&this->f.pts, this->f.sizept);
X    this->f.calc = FALSE;
X    if (this->function) free_expr(this->function);
X    if (this->derivee) free_expr(this->derivee);
X    this->function = this->derivee = NULL;
X}
X
X/* Init dependant parts of function */
Xstatic int create_r_of_t(struct r_of_t *this)
X{
X    this->f.calc = FALSE;
X    this->f.var.name = this->f.vname;
X    this->function = compile(this->expr);
X    if (eval_error != 0)
X    {
X        message(this->f.o.g, "Compilation error:", eval_messages[eval_error], (
Xchar *)NULL);
X        return FALSE;
X    }
X    this->derivee = differentiate(this->function, this->f.vname);
X    if (eval_error != NOT_DIFFERENTIABLE && eval_error != 0)
X    {
X        message(this->f.o.g, "Differentiation error:", eval_messages[eval_error
X], (char *)NULL);
X        return FALSE;
X    }
X    if (!make_var_list(this->function, &this->f.used))
X        init_var_list(&this->f.used);
X    return TRUE;
X}
X
X/* Allow user to define function */
Xstatic int edit_r_of_t(struct r_of_t *this, struct Region **ref)
X{
X    struct Requester *req;
X    struct Memory *m;
X    struct Gadget *gl = NULL, *sd, *nd;
X    char from[NBLEN], to[NBLEN], steps[INTLEN], expr[EXPRLEN], tname[VARLEN], c
Xolour[INTLEN];
X    int ret = FALSE;
X
X    *ref = NULL;
X
X    /* Create requester */
X    double2str(from, this->f.min);
X    double2str(to, this->f.max);
X    int2str(steps, this->f.steps);
X    int2str(colour, this->f.colour);
X    strcpy(expr, this->expr);
X    strcpy(tname, this->f.vname);
X
X    if ((m = NewMemory()) &&
X        (req = InitReq(50, 20, 255, 145, m)) &&
X        SetReqBorder(req, 1, m) &&
X        AddIntuiText(&req->ReqText, "Function", 95, 6, m) &&
X        AddText(&gl, 0, "r(", FALSE, tname, VARLEN, TRUE, 0, RELVERIFY, 25, 20,
X 25, 10, TRUE, m) &&
X        AddText(&gl, 0, ")=", FALSE, expr, EXPRLEN, TRUE, 0, RELVERIFY, 81, 20,
X 160, 10, TRUE, m) &&
X        AddText(&gl, 0, "from ", FALSE, from, NBLEN, TRUE, 0, RELVERIFY, 49, 40
X, 80, 10, TRUE, m) &&
X        AddText(&gl, 0, "to ", FALSE, to, NBLEN, TRUE, 0, RELVERIFY, 167, 40, 8
X0, 10, TRUE, m) &&
X        AddText(&gl, 0, "steps ", FALSE, steps, INTLEN, TRUE, 0, RELVERIFY, 57,
X 60, 32, 10, TRUE, m) &&
X        AddText(&gl, 0, "colour ", FALSE, colour, INTLEN, TRUE, 0, RELVERIFY, 1
X56, 60, 32, 10, TRUE, m) &&
X        (sd = AddOption(&gl, 0, "Show discontinuities", TRUE, this->f.showdisc
X* SELECTED, 0, 9, 80, 10, 10, m)) &&
X        (nd = AddOption(&gl, 0, "Allow flat discontinuities", TRUE, this->f.nic
Xedisc * SELECTED, 0, 9, 100, 10, 10, m)) &&
X        AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 40, 120, 65, 15, FALS
XE, m) &&
X        AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 145, 120, 65, 15
X, FALSE, m))
X    {
X        SetReqGadgets(req, gl);
X        if (ret = DoRequest(req, this->f.o.g, std_ghandler))
X        {
X            *ref = full_refresh(this->f.o.g);
X
X            /* Extract info */
X            this->f.min = str2double(from);
X            this->f.max = str2double(to);
X            this->f.steps = str2int(steps);
X            if ((this->f.colour = str2int(colour)) == INOVAL) this->f.colour =
X1;
X            this->f.showdisc = (sd->Flags & SELECTED) != 0;
X            this->f.nicedisc = (nd->Flags & SELECTED) != 0;
X            strcpy(this->expr, expr);
X            strcpy(this->f.vname, tname);
X
X            /* Create function */
X            destroy_r_of_t(this);
X            if (this->f.o.ok = r_of_t_ok(this)) this->f.o.ok = create_r_of_t(th
Xis);
X        }
X    }
X    Free(m);
X
X    return ret;
X}
X
X/* Calculate points of function */
Xstatic int calc_r_of_t(struct r_of_t *this, int allow_mes)
X{
X    double theta;
X    int i;
X    struct graph *const g = this->f.o.g;
X    int const steps = (this->f.steps == INOVAL ? DEFSTEPS : this->f.steps) - 1;
X     
X    double const step = (this->f.max - this->f.min) / steps;
X    char func[FNAMELEN + 30];
X
X    new_list(&this->f.pts);
X
X    strcpy(func, "Can't calculate points for ");
X    strcat(func, this->f.o.name);
X    strcat(func, ":");
X
X    if (!create_quick(&this->f.var))
X    {
X        if (allow_mes) message(g, func, "Couldn't create variable", (char *)NUL
XL);
X        else alert(g->io.win, func, "Couldn't create variable");
X        return FALSE;
X    }
X
X    /* Calculate steps points, spread evenly from min to max */
X    for (i = 0, theta = this->f.min; i <= steps; i++, theta += step)
X    {
X        point_r_of_t *pt = alloc_node(this->f.sizept);
X
X        if (!pt)
X        { /* No mem */
X            free_list(&this->f.pts, this->f.sizept);
X            free_quick(&this->f.var);
X            if (allow_mes) message(g, func, "No memory", (char *)NULL);
X            else alert(g->io.win, func, "No memory");
X            return FALSE;
X        }
X        add_tail(&this->f.pts, pt);
X
X        pt->theta = theta;
X        set_quick(&this->f.var, theta);
X        pt->r = quick_eval(this->function);
X        pt->p.state = (eval_error == 0) ? EXISTS : 0;
X        /* Polar -> Rect conversion */
X        pt->p.x = fabs(pt->r) * cos(theta); pt->p.y = fabs(pt->r) * sin(theta);
X     
X    }
X    free_quick(&this->f.var);
X    return TRUE;
X}
X
X/* Try to improve look of function by adding points. If fails, decides that
X   there is a discontinuity */
X/* see f_of_x.c for details */
Xstatic struct Region *improve_r_of_t(struct r_of_t *this)
X{
X    struct graph *const g = this->f.o.g;
X    point_r_of_t *pt, *next;
X    int ok = FALSE, iter, abort = FALSE;
X    double flatx = FLAT * (g->a.y.max - g->a.y.min) / g->io.win->Height;
X    double flaty = FLAT * (g->a.x.max - g->a.x.min) / g->io.win->Width;
X    char msg[FNAMELEN + 30];
X    char pass[20];
X    struct Requester *req;
X    struct Region *full = NULL;
X
X    /* Flat has no meaning when graph incorrect */
X    if (!this->f.o.g->ok) flatx = flaty = 0.0;
X
X    if (!this->f.calc)
X    {
X        strcpy(msg, this->f.o.name);
X        strcpy(msg, "not calculated!");
X        message(g, msg, (char *)NULL);
X        return NULL;
X    }
X    if (!this->derivee)
X    {
X        strcpy(msg, this->f.o.name);
X        strcat(msg, " wasn't differentiable");
X        message(g, msg, (char *)NULL);
X        return NULL;
X    }
X    if (!create_quick(&this->f.var))
X    {
X        message(g, "Couldn't create variable", (char *)NULL);
X        return NULL;
X    }
X
X    if (!(req = abort_request(g, "Improve: Pass 1")))
X        message(g, "No Memory !", (char *)NULL);
X    else
X    {
X        full = full_refresh(this->f.o.g);
X
X        for (iter = 1; iter <= MAXITER && !ok && !abort; iter++)
X        {
X            sprintf(pass, "Improve: Pass %d", iter);
X            set_abort_msg(req, pass);
X            ok = TRUE;
X
X            for (pt = first(&this->f.pts); succ(next = succ(pt)); pt = next)
X            {
X                if (aborted(req)) { abort = TRUE; break; }
X
X                if ((pt->p.state & (EXISTS | OK)) == EXISTS) /* Only exists */
X                {
X                    double dx, dy, dr, dtheta;
X
X                    pt->p.state |= OK;
X                    pt->p.state &= ~DISC;
X
X                    dtheta = next->theta - pt->theta;
X                    set_quick(&this->f.var, pt->theta);
X                    dr = quick_eval(this->derivee);
X                    if (eval_error == 0)
X                    {
X                        double c = cos(pt->theta);
X                        double s = sin(pt->theta);
X
X                        /* A little elementary calculus ... */
X                        dx = dr * c - pt->r * s;
X                        dy = dr * s + pt->r * c;
X
X                        if (eval_error == 0)
X                        {
X                            double ecartx = next->p.x - pt->p.x;
X                            double errorx = fabs(ecartx - dtheta * dx);
X                            double ecarty = next->p.y - pt->p.y;
X                            double errory = fabs(ecarty - dtheta * dy);
X
X                            /* Check both axes */
X                            if ((errorx > fabs(ecartx) * MAXERROR && (!this->f.
Xnicedisc || errorx > flatx)) ||
X                                (errory > fabs(ecarty) * MAXERROR && (!this->f.
Xnicedisc || errory > flaty)))
X                            {
X                                pt->p.state &= ~OK;
X                                ok = FALSE;
X
X                                if (iter == MAXITER) pt->p.state |= DISC;
X                                else /* cut interval in 2 */
X                                {
X                                    point_r_of_t *newpt = alloc_node(this->f.si
Xzept);
X
X                                    if (!newpt)
X                                    {
X                                        nomem(g->io.win);
X                                        abort = TRUE;
X                                        break;
X                                    }
X
X                                    newpt->theta = (pt->theta + next->theta) /
X2;
X                                    set_quick(&this->f.var, newpt->theta);
X                                    newpt->r = quick_eval(this->function);
X                                    newpt->p.state = (eval_error == 0) ? EXISTS
X : 0;
X                                    newpt->p.x = fabs(newpt->r) * cos(newpt->th
Xeta);
X                                    newpt->p.y = fabs(newpt->r) * sin(newpt->th
Xeta);
X                                    insert(&this->f.pts, newpt, pt);
X                                }
X                            }
X                        }
X                    }
X                }
X            }
X        }
X        end_abort_request(req);
X    }
X    free_quick(&this->f.var);
X    return full;
X}
X
X/* String representation of function */
Xstatic char *f2str_r_of_t(struct r_of_t *this, char *buf, int maxlen)
X{
X    buf[maxlen - 1] = '\0';
X    strncpy(buf, "polar ", maxlen - 1);
X    strncat(buf, this->f.o.name, maxlen - strlen(buf) - 1);
X    strncat(buf, "(", maxlen - strlen(buf) - 1);
X    strncat(buf, this->f.vname, maxlen - strlen(buf) - 1);
X    strncat(buf, ")=", maxlen - strlen(buf) - 1);
X    strncat(buf, this->expr, maxlen - strlen(buf) - 1);
X
X    return buf;
X}
X
Xstatic int save_r_of_t(struct r_of_t *this, FILE *f)
X{
X    short tag = R_OF_T_TAG;
X    short end = R_OF_T_END;
X
X    return WRITE(f, tag) &&
X           WRITE(f, this->expr) &&
X           WRITE(f, end);
X}
X
X/* free function */
Xstatic struct Region *delete_r_of_t(struct r_of_t *this)
X{
X    struct Region *full = full_refresh(this->f.o.g);
X
X    destroy_r_of_t(this);
X    FreeMem(this, sizeof(struct r_of_t));
X
X    return full;
X}
X
X/* Create a new function */
Xstruct r_of_t *new_r_of_t(struct graph *g, char *name)
X{
X    struct r_of_t *this = AllocMem(sizeof(struct r_of_t), MEMF_CLEAR);
X
X    if (this)
X    {
X        /* Standard init */
X        init_function(&this->f, g, name);
X        /* Local methods */
X        this->f.o.delete = (void *)delete_r_of_t;
X        this->f.o.edit = (void *)edit_r_of_t;
X        this->f.o.improve = (void *)improve_r_of_t;
X        this->f.o.f2str = (void *)f2str_r_of_t;
X        this->f.calcf = (void *)calc_r_of_t;
X        this->f.save = (void *)save_r_of_t;
X        this->f.sizept = sizeof(point_r_of_t);
X        init_var_list(&this->f.used);
X        return this;
X    }
X    message(g, "Couldn't create function:", "No memory", (char *)NULL);
X    return NULL;
X}
X
X/* Save local data to file */
X/* load from file */
Xstruct r_of_t *load_r_of_t(struct graph *g, FILE *f)
X{
X    struct r_of_t *this = new_r_of_t(g, "");
X
X    if (this)
X    {
X        short end;
X
X        /* Read local data */
X        if (READ(f, this->expr) &&
X            READ(f, end) &&
X            end == R_OF_T_END)
X        {
X            load_rest(&this->f, f); /* Read standard data */
X            if (this->f.o.ok = r_of_t_ok(this)) this->f.o.ok = create_r_of_t(th
Xis);
X
X            return this;
X        }
X        delete_r_of_t(this);
X    }
X    return NULL;
X}
X
SHAR_EOF
echo "extracting r_t.c"
sed 's/^X//' << \SHAR_EOF > r_t.c
X/*
X *                 GRAPH, Version 1.00 - 4 August 1989
X *
X *            Copyright 1989, David Gay. All Rights Reserved.
X *            This software is freely redistrubatable.
X */
X
X#include <exec/types.h>
X#include <intuition/intuition.h>
X#include <graphics/text.h>
X#include <math.h>
X#include <string.h>
X
X#include "object.h"
X#include "object/function.h"
X#include "file.h"
X#include "graph.h"
X#include "uio.h"
X#include "coords.h"
X#include "list.h"
X#include "grph.h"
X#include "user/eval.h"
X#include "user/gadgets.h"
X#include "tracker.h"
X
X#include <proto/exec.h>
X#include <proto/intuition.h>
X#include <proto/graphics.h>
X
X/* (private) class r_t, inherits from function */
Xstruct r_t {
X    struct function f;
X    char r[EXPRLEN], theta[EXPRLEN];
X    value r_t, theta_t, dr, dtheta;
X};
X
X/* (private) class, inherits from point */
Xstruct point_r_t {
X    point p;
X    double r, theta, t;
X};
X
Xtypedef struct point_r_t point_r_t;
X
X/*-------------------------------------------------------------------------*/
X/*                        r_t class implementation                         */
X/*-------------------------------------------------------------------------*/
X
X/* Is the function displayable ? */
Xstatic int r_t_ok(const struct r_t *this)
X{
X    return this->f.min != NOVAL && this->f.max != NOVAL &&
X           this->f.min < this->f.max &&
X           (this->f.steps == INOVAL || this->f.steps >= 3);
X}
X
X/* Free resources */
Xstatic void destroy_r_t(struct r_t *this)
X{
X    free_var_list(&this->f.used);
X    if (this->f.calc) free_list(&this->f.pts, this->f.sizept);
X    this->f.calc = FALSE;
X    if (this->r_t) free_expr(this->r_t);
X    if (this->dr) free_expr(this->dr);
X    if (this->theta_t) free_expr(this->theta_t);
X    if (this->dtheta) free_expr(this->dtheta);
X    this->theta_t = this->r_t = this->dtheta = this->dr = NULL;
X}
X
X/* Init dependant parts of function */
Xstatic int create_r_t(struct r_t *this)
X{
X    this->f.calc = FALSE;
X    this->f.var.name = this->f.vname;
X    this->r_t = compile(this->r);
X    if (eval_error != 0)
X    {
X        message(this->f.o.g, "Compilation error in x(t):", eval_messages[eval_e
Xrror], (char *)NULL);
X        return FALSE;
X    }
X    this->dr = differentiate(this->r_t, this->f.vname);
X    if (eval_error != NOT_DIFFERENTIABLE && eval_error != 0)
X    {
X        message(this->f.o.g, "Differentiation error (x):", eval_messages[eval_e
Xrror], (char *)NULL);
X        return FALSE;
X    }
X    this->theta_t = compile(this->theta);
X    if (eval_error != 0)
X    {
X        message(this->f.o.g, "Compilation error in y(t):", eval_messages[eval_e
Xrror], (char *)NULL);
X        return FALSE;
X    }
X    this->dtheta = differentiate(this->theta_t, this->f.vname);
X    if (eval_error != NOT_DIFFERENTIABLE && eval_error != 0)
X    {
X        message(this->f.o.g, "Differentiation error(y):", eval_messages[eval_er
Xror], (char *)NULL);
X        return FALSE;
X    }
X    if (!make_var_list(this->r_t, &this->f.used) || !make_var_list(this->theta_
Xt, &this->f.used))
X        init_var_list(&this->f.used);
X    return TRUE;
X}
X
X/* Allow user to define function */
Xstatic int edit_r_t(struct r_t *this, struct Region **ref)
X{
X    struct Requester *req;
X    struct Memory *m;
X    struct Gadget *gl = NULL, *sd, *nd;
X    char from[NBLEN], to[NBLEN], steps[INTLEN], r[EXPRLEN], theta[EXPRLEN], tna
Xme[VARLEN], colour[INTLEN];
X    int ret = FALSE;
X
X    *ref = NULL;
X
X    /* Create requester */
X    double2str(from, this->f.min);
X    double2str(to, this->f.max);
X    int2str(steps, this->f.steps);
X    int2str(colour, this->f.colour);
X    strcpy(r, this->r);
X    strcpy(theta, this->theta);
X    strcpy(tname, this->f.vname);
X
X    if ((m = NewMemory()) &&
X        (req = InitReq(50, 20, 255, 165, m)) &&
X        SetReqBorder(req, 1, m) &&
X        AddIntuiText(&req->ReqText, "Function", 95, 6, m) &&
X        AddText(&gl, 0, "r(", FALSE, tname, VARLEN, TRUE, 0, RELVERIFY, 27, 20,
X 24, 10, TRUE, m) &&
X        AddText(&gl, 0, ")=", FALSE, r, EXPRLEN, TRUE, 0, RELVERIFY, 75, 20, 16
X0, 10, TRUE, m) &&
X        AddText(&gl, 0, "theta()=", FALSE, theta, EXPRLEN, TRUE, 0, RELVERIFY,
X75, 40, 160, 10, TRUE, m) &&
X        AddText(&gl, 0, "from ", FALSE, from, NBLEN, TRUE, 0, RELVERIFY, 51, 60
X, 80, 10, TRUE, m) &&
X        AddText(&gl, 0, "to ", FALSE, to, NBLEN, TRUE, 0, RELVERIFY, 169, 60, 8
X0, 10, TRUE, m) &&
X        AddText(&gl, 0, "steps ", FALSE, steps, INTLEN, TRUE, 0, RELVERIFY, 59,
X 80, 32, 10, TRUE, m) &&
X        AddText(&gl, 0, "colour ", FALSE, colour, INTLEN, TRUE, 0, RELVERIFY, 1
X56, 80, 32, 10, TRUE, m) &&
X        (sd = AddOption(&gl, 0, "Show discontinuities", TRUE, this->f.showdisc
X* SELECTED, 0, 11, 100, 10, 10, m)) &&
X        (nd = AddOption(&gl, 0, "Allow flat discontinuities", TRUE, this->f.nic
Xedisc * SELECTED, 0, 11, 120, 10, 10, m)) &&
X        AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 40, 140, 65, 15, FALS
XE, m) &&
X        AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 145, 140, 65, 15
X, FALSE, m))
X    {
X        SetReqGadgets(req, gl);
X        if (ret = DoRequest(req, this->f.o.g, std_ghandler))
X        {
X            *ref = full_refresh(this->f.o.g);
X
X            /* Extract info */
X            this->f.min = str2double(from);
X            this->f.max = str2double(to);
X            this->f.steps = str2int(steps);
X            if ((this->f.colour = str2int(colour)) == INOVAL) this->f.colour =
X1;
X            this->f.showdisc = (sd->Flags & SELECTED) != 0;
X            this->f.nicedisc = (nd->Flags & SELECTED) != 0;
X            strcpy(this->r, r);
X            strcpy(this->theta, theta);
X            strcpy(this->f.vname, tname);
X
X            /* Create function */
X            destroy_r_t(this);
X            if (this->f.o.ok = r_t_ok(this)) this->f.o.ok = create_r_t(this);
X        }
X    }
X    Free(m);
X
X    return ret;
X}
X
X/* Calculate points of function */
Xstatic int calc_r_t(struct r_t *this, int allow_mes)
X{
X    double t;
X    int i;
X    struct graph *const g = this->f.o.g;
X    double const tmin = this->f.min;
X    double const tmax = this->f.max;
X    int const steps = this->f.steps == INOVAL ? DEFSTEPS : this->f.steps;
X    double const step = (tmax - tmin) / (steps - 1);
X    static char func[FNAMELEN + 30];
X
X    strcpy(func, "Can't calculate points for ");
X    strcat(func, this->f.o.name);
X    strcat(func, ":");
X
X    new_list(&this->f.pts);
X
X    if (!create_quick(&this->f.var))
X    {
X        if (allow_mes) message(g, func, "Couldn't create variable", (char *)NUL
XL);
X        else alert(g->io.win, func, "Couldn't create variable");
X        return FALSE;
X    }
X
X    /* Calculate steps points, spread evenly from min to max */
X    for (i = 0, t = tmin; i < steps; i++, t += step)
X    {
X        point_r_t *pt = alloc_node(this->f.sizept);
X
X        if (!pt)
X        { /* No mem */
X            free_list(&this->f.pts, this->f.sizept);
X            free_quick(&this->f.var);
X            if (allow_mes) message(g, func, "No memory", (char *)NULL);
X            else alert(g->io.win, func, "No memory");
X            return FALSE;
X        }
X        add_tail(&this->f.pts, pt);
X
X        set_quick(&this->f.var, t);
X        pt->t = t;
X
X        pt->r = quick_eval(this->r_t);
X        pt->p.state = (eval_error == 0) ? EXISTS : 0;
X        if (pt->p.state == EXISTS)
X        {
X            pt->theta = quick_eval(this->theta_t);
X            pt->p.state = (eval_error == 0) ? EXISTS : 0;
X            /* Polar -> Rect conversion */
X            pt->p.x = fabs(pt->r) * cos(pt->theta);
X            pt->p.y = fabs(pt->r) * sin(pt->theta);
X        }
X    }
X    free_quick(&this->f.var);
X    return TRUE;
X}
X
X/* Try to improve look of function by adding points. If fails, decides that
X   there is a discontinuity */
X/* see f_of_x.c for details */
Xstatic struct Region *improve_r_t(struct r_t *this)
X{
X    struct graph *const g = this->f.o.g;
X    point_r_t *pt, *next;
X    int ok = FALSE, iter, abort = FALSE;
X    double flatx = FLAT * (g->a.y.max - g->a.y.min) / g->io.win->Height;
X    double flaty = FLAT * (g->a.x.max - g->a.x.min) / g->io.win->Width;
X    char msg[FNAMELEN + 30];
X    char pass[20];
X    struct Requester *req;
X    struct Region *full = NULL;
X
X    /* Flat has no meaning when graph incorrect */
X    if (!this->f.o.g->ok) flatx = flaty = 0.0;
X
X    if (!this->f.calc)
X    {
X        strcpy(msg, this->f.o.name);
X        strcpy(msg, "not calculated!");
X        message(g, msg, (char *)NULL);
X        return NULL;
X    }
X    if (!this->dr || !this->dtheta)
X    {
X        strcpy(msg, this->f.o.name);
X        strcat(msg, " wasn't differentiable");
X        message(g, msg, (char *)NULL);
X        return NULL;
X    }
X    if (!create_quick(&this->f.var))
X    {
X        message(g, "Couldn't create variable", (char *)NULL);
X        return NULL;
X    }
X
X    if (!(req = abort_request(g, "Improve: Pass 1")))
X        message(g, "No Memory !", (char *)NULL);
X    else
X    {
X        full = full_refresh(this->f.o.g);
X
X        for (iter = 1; iter <= MAXITER && !ok && !abort; iter++)
X        {
X            sprintf(pass, "Improve: Pass %d", iter);
X            set_abort_msg(req, pass);
X            ok = TRUE;
X
X            for (pt = first(&this->f.pts); succ(next = succ(pt)); pt = next)
X            {
X                if ((pt->p.state & (EXISTS | OK)) == EXISTS) /* Only exists */
X                {
X                    double dr, dtheta, dt;
X
X                    pt->p.state |= OK;
X                    pt->p.state &= ~DISC;
X
X                    dt = next->t - pt->t;
X                    set_quick(&this->f.var, pt->t);
X                    dr = quick_eval(this->dr);
X                    if (eval_error == 0)
X                    {
X                        dtheta = quick_eval(this->dtheta);
X                        if (eval_error == 0)
X                        {
X                            double c = cos(pt->theta);
X                            double s = sin(pt->theta);
X                            /* A little elementary calculus ... */
X                            double dx = dr * c - pt->r * dtheta * s;
X                            double dy = dr * s + pt->r * dtheta * c;
X                            double ecartx = next->p.x - pt->p.x;
X                            double errorx = fabs(ecartx - dt * dx);
X                            double ecarty = next->p.y - pt->p.y;
X                            double errory = fabs(ecarty - dt * dy);
X
X                            /* Check both axes */
X                            if ((errorx > fabs(ecartx) * MAXERROR && (!this->f.
Xnicedisc || errorx > flatx)) ||
X                                (errory > fabs(ecarty) * MAXERROR && (!this->f.
Xnicedisc || errory > flaty)))
X                            {
X                                pt->p.state &= ~OK;
X                                ok = FALSE;
X
X                                if (iter == MAXITER) pt->p.state |= DISC;
X                                else /* cut interval in 2 */
X                                {
X                                    point_r_t *newpt = alloc_node(this->f.sizep
Xt);
X
X                                    if (!newpt)
X                                    {
X                                        nomem(g->io.win);
X                                        abort = TRUE;
X                                        break;
X                                    }
X
X                                    newpt->t = (pt->t + next->t) / 2;
X                                    set_quick(&this->f.var, newpt->t);
X
X                                    newpt->r = quick_eval(this->r_t);
X                                    newpt->p.state = (eval_error == 0) ? EXISTS
X : 0;
X                                    if (newpt->p.state == EXISTS)
X                                    {
X                                        newpt->theta = quick_eval(this->theta_t
X);
X                                        newpt->p.state = (eval_error == 0) ? EX
XISTS : 0;
X                                        newpt->p.x = fabs(newpt->r) * cos(newpt
X->theta);
X                                        newpt->p.y = fabs(newpt->r) * sin(newpt
X->theta);
X                                    }
X                                    insert(&this->f.pts, newpt, pt);
X                                }
X                            }
X                        }
X                    }
X                }
X            }
X        }
X        end_abort_request(req);
X    }
X    free_quick(&this->f.var);
X    return full;
X}
X
X/* String representation of function */
Xstatic char *f2str_r_t(struct r_t *this, char *buf, int maxlen)
X{
X    buf[maxlen - 1] = '\0';
X    strncpy(buf, this->f.o.name, maxlen - 1);
X    strncat(buf, ": r(", maxlen - strlen(buf) - 1);
X    strncat(buf, this->f.vname, maxlen - strlen(buf) - 1);
X    strncat(buf, ")=", maxlen - strlen(buf) - 1);
X    strncat(buf, this->r, maxlen - strlen(buf) - 1);
X    strncat(buf, ", theta(", maxlen - strlen(buf) - 1);
X    strncat(buf, this->f.vname, maxlen - strlen(buf) - 1);
X    strncat(buf, ")=", maxlen - strlen(buf) - 1);
X    strncat(buf, this->theta, maxlen - strlen(buf) - 1);
X
X    return buf;
X}
X
X/* Save local data to file */
Xstatic int save_r_t(struct r_t *this, FILE *f)
X{
X    short tag = R_T_TAG;
X    short end = R_T_END;
X
X    return WRITE(f, tag) &&
X           WRITE(f, this->r) &&
X           WRITE(f, this->theta) &&
X           WRITE(f, end);
X}
X
X/* free function */
Xstatic struct Region *delete_r_t(struct r_t *this)
X{
X    struct Region *full = full_refresh(this->f.o.g);
X
X    destroy_r_t(this);
X    FreeMem(this, sizeof(struct r_t));
X
X    return full;
X}
X
X/* Create a new function */
Xstruct r_t *new_r_t(struct graph *g, char *name)
X{
X    struct r_t *this = AllocMem(sizeof(struct r_t), MEMF_CLEAR);
X
X    if (this)
X    {
X        /* Standard init */
X        init_function(&this->f, g, name);
X        /* Local methods */
X        this->f.o.delete = (void *)delete_r_t;
X        this->f.o.edit = (void *)edit_r_t;
X        this->f.o.improve = (void *)improve_r_t;
X        this->f.o.f2str = (void *)f2str_r_t;
X        this->f.calcf = (void *)calc_r_t;
X        this->f.save = (void *)save_r_t;
X        this->f.sizept = sizeof(point_r_t);
X        return this;
X    }
X    message(g, "Couldn't create function:", "No memory", (char *)NULL);
X    return NULL;
X}
X
X/* load from file */
Xstruct r_t *load_r_t(struct graph *g, FILE *f)
X{
X    struct r_t *this = new_r_t(g, "");
X
X    if (this)
X    {
X        short end;
X
X        /* Read local data */
X        if (READ(f, this->r) &&
X            READ(f, this->theta) &&
X            READ(f, end) &&
X            end == R_T_END)
X        {
X            load_rest(&this->f, f); /* Read standard data */
X            if (this->f.o.ok = r_t_ok(this)) this->f.o.ok = create_r_t(this);
X
X            return this;
X        }
X        delete_r_t(this);
X    }
X    return NULL;
X}
X
SHAR_EOF
echo "extracting smallsym.c"
sed 's/^X//' << \SHAR_EOF > smallsym.c
X#include <exec/types.h>
X#include <exec/nodes.h>
X#include <exec/errors.h>
X#include <exec/lists.h>
X#include <exec/io.h>
X#include <exec/ports.h>
X#include <exec/memory.h>
X
X#include <intuition/preferences.h>
X#include <intuition/screens.h>
X#include <intuition/intuition.h>
X
X#include <devices/timer.h>
X#include <devices/printer.h>
X#include <devices/serial.h>
X#include <devices/clipboard.h>
X#include <devices/input.h>
X#include <devices/trackdisk.h>
X#include <devices/inputevent.h>
X#include <devices/prtgfx.h>
X
X#include <proto/console.h>
X#include <proto/diskfont.h>
X#include <proto/dos.h>
X#include <proto/exec.h>
X#include <proto/intuition.h>
X#include <proto/graphics.h>
X#include <proto/icon.h>
X#include <proto/layers.h>
X#include <proto/timer.h>
X
X#include <libraries/dos.h>
X#include <libraries/dosextens.h>
X#include <libraries/diskfont.h>
X
X#include <workbench/startup.h>
X#include <workbench/workbench.h>
X
X#include <graphics/regions.h>
X#include <graphics/gfxmacros.h>
X#include <graphics/rastport.h>
X#include <graphics/layers.h>
X#include <graphics/gfx.h>
X#include <graphics/text.h>
X#include <graphics/clip.h>
SHAR_EOF
echo "extracting tracker.c"
sed 's/^X//' << \SHAR_EOF > tracker.c
X/* Resource tracking routines, by Karl Lehenbauer
X   Lattice version by David Gay.
X   This code is in the public domain
X*/
X
X/* tracking memory allocator */
X
X#include <exec/types.h>
X#include <exec/memory.h>
X#include <stdio.h>
X#include <string.h>
X
X#include <proto/exec.h>
X#include <proto/dos.h>
X
X/* comment out the following line if you want locks freed twice reported
X * at the cost of getting spurious resource error messages when
X * reusing the lock */
X/* #define FORGET_LOCKS_WHEN_UNLOCKED */
X
X/* comment out the following line if you want memory freed twice reported
X * at the cost of getting spurious resource error messages when
X * freeing and reallocating memory */
X#define FORGET_MEMORY_WHEN_FREED
X
X/* make sure our invocations of the real routines on behalf of the user
X   don't cause us to recurse */
X
X#ifdef AllocMem
X#undef AllocMem
X#undef FreeMem
X#undef AllocSignal
X#undef FreeSignal
X#undef Lock
X#undef UnLock
X#undef DupLock
X#undef ParentDir
X#endif
X
X/* my flags */
X#define FREED_IT 1
X
Xstruct TrackingAllocMemData
X{
X    UBYTE *where;             /* address returned by allocator */
X    long amount;             /* number of bytes allocated */
X    long alloc_flags;         /* flags passed to allocator */
X    char *file;         /* filename of caller from the macro */
X    int line;                 /* line number of caller from macro */
X    long my_flags;             /* flags internal to tracker */
X    struct TrackingAllocMemData *next;    /* pointer to next entry */
X};
X
Xstruct TrackingAllocMemData *TrackingAllocMemList = NULL;
Xlong MemAllocCount = 0, MemFreeCount = 0;
X
Xvoid *TrackingAllocMem(amount,flags,file,line)
Xlong amount;
Xlong flags;
Xchar *file;
Xint line;
X{
X    struct TrackingAllocMemData *rp;
X    UBYTE *users_memory;
X
X    /* perform the actual alloc */
X    users_memory = AllocMem(amount,flags);
X
X    /* if it succeeded, record tracking info */
X    if (users_memory)
X    {
X         MemAllocCount++;
X
X         if ((rp = AllocMem((long)sizeof(struct TrackingAllocMemData),0L)) == N
XULL)
X             fprintf(stderr, "tracker: can't alloc memory to record AllocMem da
Xta");
X
X         /* add new alloc data entry to linked list */
X         rp->next = TrackingAllocMemList;
X         TrackingAllocMemList = rp;
X
X         /* shove in save values */
X         rp->amount = amount;
X         rp->alloc_flags = flags;
X         rp->where = users_memory;
X         rp->file = file;
X         rp->line = line;
X         rp->my_flags = 0;
X    }
X    /* return pointer to the space allocated */
X    return(users_memory);
X}
X
Xvoid TrackingFreeMem(where,amount,file,line)
XUBYTE *where;
Xlong amount;
Xchar *file;
Xint line;
X{
X    struct TrackingAllocMemData *rp, *op, *freep;
X
X    MemFreeCount++;
X    /* scan the memory tracking list for a match */
X    for (rp = TrackingAllocMemList, op = NULL; rp != NULL; op = rp, rp = rp->ne
Xxt)
X    {
X         /* if we matched the address */
X         if (rp->where == where)
X         {
X             /* if they got the amount wrong, tell them */
X             if (rp->amount != amount)
X             {
X                 fprintf(stderr,"freed addr %lx OK but length differs, talloc'e
Xd %ld, freed %ld,\n\tallocated at file %s line %d, freed at file %s line %d\n",
X                 where,rp->amount,amount,rp->file,rp->line,file,line);
X             }
X#ifndef FORGET_MEMORY_WHEN_FREED
X             /* if it's already free, tell them they freed twice */
X             if (rp->my_flags & FREED_IT)
X             {
X                 fprintf(stderr,"freed memory twice at %lx, amount %ld,\n\tallo
Xcated in file %s at line %d, freed in file %s at line %d\n",where,amount,rp->fil
Xe,rp->line,file,line);
X                 return;
X             }
X             else
X             {
X                 /* mark this entry as free */
X                 rp->my_flags |= FREED_IT;
X             }
X#else
X             /* remove entry from linked list and free it */
X             if (op != NULL) op->next = rp->next;
X             else
X                 TrackingAllocMemList = rp->next;
X             freep = rp;
X             rp = rp->next;
X             FreeMem(freep,(long)sizeof(struct TrackingAllocMemData));
X#endif
X             /* Munge memory block */
X             memset((char *)where, amount, 3); /* Set to an odd number ... */
X             FreeMem(where,(long)amount);
X
X             return;
X         }
X    }
X    fprintf(stderr,"Freed memory at %lx of amount %ld that wasn't allocated,\n\
Xtfreed at file %s line %d\n",where,amount,file,line);
X    FreeMem(where,amount);
X}
X
Xvoid ReportUnfreedMemory()
X{
X    struct TrackingAllocMemData *rp = TrackingAllocMemList, *freep;
X
X    while (rp != NULL)
X    {
X         if (!(rp->my_flags & FREED_IT))
X         {
X             fprintf(stderr,"FreeMem was never called for memory at %lx, amount
X %ld,\n\tthe alloc was performed at file %s line %d\n",rp->where,rp->amount,rp->
Xfile,rp->line);
X         }
X         freep = rp;
X         rp = rp->next;
X         FreeMem(freep,(long)sizeof(struct TrackingAllocMemData));
X    }
X    printf("Total tracked AllocMem calls %ld, FreeMem calls %ld\n",MemAllocCoun
Xt,MemFreeCount);
X}
X
X
X/* track signals */
X/* tracking AllocSignal doesn't currently track where it was called from */
X
Xlong TrackingSignalMask = 0;
Xlong SignalAllocCount = 0, SignalFreeCount = 0;
X
Xlong TrackingAllocSignal(signal_num,file,line)
Xlong signal_num;
Xchar *file;
Xint line;
X{
X    SignalAllocCount++;
X
X    signal_num = AllocSignal(signal_num);
X
X    if (signal_num != -1)
X         TrackingSignalMask |= (1 << signal_num);
X
X    return(signal_num);
X}
X
Xvoid TrackingFreeSignal(signal_num,file,line)
Xlong signal_num;
Xchar *file;
Xint line;
X{
X    SignalFreeCount++;
X
X    if (!(TrackingSignalMask & (1 << signal_num)))
X    {
X         fprintf(stderr, "freed a signal (%ld) that was never allocated, at fil
Xe %s line %d\n",
X             signal_num,file,line);
X    }
X    TrackingSignalMask &= ~(1 << signal_num);
X}
X
Xvoid ReportUnfreedSignals()
X{
X    if (TrackingSignalMask)
X         fprintf(stderr, "failed to free signals indicated by this mask: %8lx\n
X",
X             TrackingSignalMask);
X    printf("Total tracked AllocSignal calls %ld, FreeSignal calls %ld\n",Signal
XAllocCount,SignalFreeCount);
X}
X
X/* tracking lock and unlock */
X
Xstruct TrackingLockData
X{
X    BPTR lock;    /* lock returned by Lock */
X    char *name;         /* name of file that was locked */
X    long accessMode;         /* access mode of the file that was locked */
X    char *file;         /* ptr to file name of line of caller */
X    int line;                 /* ptr to line number text of locker */
X    long my_flags;             /* flags internal to tracker */
X    struct TrackingLockData *next;    /* pointer to next entry */
X};
X
X/* flags */
X#define CREATED_BY_DUPLOCK 1
X
Xstruct TrackingLockData *TrackingLockList = NULL;
Xlong TrackerLockCount = 0, TrackerUnLockCount = 0;
X
XBPTR TheTrackingLock(name, accessMode, file, line)
Xchar *name;
Xlong accessMode;
Xchar *file;
Xint line;
X{
X    struct TrackingLockData *lp;
X    BPTR users_lock;
X
X    users_lock = Lock(name, (long)accessMode);
X
X    if (users_lock)
X    {
X         TrackerLockCount++;
X
X         if ((lp = AllocMem((long)sizeof(struct TrackingLockData),0L)) == NULL)
X     
X             fprintf(stderr, "tracker: can't alloc memory to record lock data")
X;
X
X         /* add new alloc data entry to linked list */
X         lp->next = TrackingLockList;
X         TrackingLockList = lp;
X
X         /* shove in save values */
X         lp->accessMode = accessMode;
X         lp->file = file;
X         lp->line = line;
X         lp->my_flags = 0;
X         lp->lock = users_lock;
X
X         /* alloc space for filename and save */
X         if ((lp->name = AllocMem((long)(strlen(name)+1),0L)) == NULL)
X             fprintf(stderr, "tracker: can't alloc memory to record lock filena
Xme");
X         strcpy(lp->name,name);
X    }
X    return(users_lock);
X}
X
XBPTR TheTrackingDupLock(lock, file, line)
XBPTR lock;
Xchar *file;
Xint line;
X{
X    struct TrackingLockData *lp;
X    BPTR users_lock;
X
X    users_lock = DupLock(lock);
X
X    if (users_lock)
X    {
X         TrackerLockCount++;
X
X         if ((lp = AllocMem((long)sizeof(struct TrackingLockData),0L)) == NULL)
X     
X             fprintf(stderr, "tracker: can't alloc memory to record lock data")
X;
X
X         /* add new alloc data entry to linked list */
X         lp->next = TrackingLockList;
X         TrackingLockList = lp;
X
X         lp->file = file;
X         lp->line = line;
X         lp->name = NULL;
X         lp->lock = users_lock;
X         lp->my_flags = CREATED_BY_DUPLOCK;
X    }
X    return(users_lock);
X}
X
Xvoid TheTrackingUnLock(lock,file,line)
XBPTR lock;
Xchar *file;
Xint line;
X{
X    struct TrackingLockData *lp, *op, *freep;
X
X    TrackerUnLockCount++;
X
X    /* scan the lock tracking list for a match */
X    for (lp = TrackingLockList, op = NULL; lp != NULL; op = lp, lp = lp->next)
X    {
X         /* if we matched the lock */
X         if (lp->lock == lock)
X         {
X#ifndef FORGET_LOCKS_WHEN_UNLOCKED
X             /* if it's already free, tell them they freed twice */
X             if (lp->my_flags & FREED_IT)
X             {
X                 fprintf(stderr,"freed lock twice, lock %lx, filename %s\n\tloc
Xked at file %s line %d, freed at file %s line %d\n",lock,lp->name,lp->file,lp->l
Xine,file,line);
X                 return;
X             }
X             else
X             {
X                 /* mark this entry as free */
X                 lp->my_flags |= FREED_IT;
X             }
X#else
X             if (op != NULL) op->next = lp->next;
X             else TrackingLockList = lp->next;
X             freep = lp;
X             lp = lp->next;
X             if (lp->name != NULL)
X                 FreeMem(lp->name,(long)(strlen(lp->name)+1));
X             FreeMem(freep,(long)(sizeof(struct TrackingLockData)));
X#endif
X             UnLock(lock);
X             return;
X         }
X    }
X    fprintf(stderr,"Freed lock %lx that hadn't been allocated at file %s line %
Xd\n",lock,file,line);
X}
X
XBPTR TheTrackingParentDir(BPTR lock, char *file, int line)
X{
X    struct TrackingLockData *lp;
X    BPTR users_lock;
X
X    users_lock = ParentDir(lock);
X
X    if (users_lock)
X    {
X         TrackerLockCount++;
X
X         if ((lp = AllocMem((long)sizeof(struct TrackingLockData),0L)) == NULL)
X     
X             fprintf(stderr, "tracker: can't alloc memory to record lock data")
X;
X
X         /* add new alloc data entry to linked list */
X         lp->next = TrackingLockList;
X         TrackingLockList = lp;
X
X         /* shove in save values */
X         lp->accessMode = 0;
X         lp->file = file;
X         lp->line = line;
X         lp->my_flags = 0;
X         lp->lock = users_lock;
X         lp->name = NULL;
X    }
X    return(users_lock);
X}
X
Xvoid ReportUnfreedLocks()
X{
X    struct TrackingLockData *lp = TrackingLockList, *freep;
X
X    while (lp != NULL)
X    {
X         if (!(lp->my_flags & FREED_IT))
X         {
X             if (lp->my_flags & CREATED_BY_DUPLOCK)
X             {
X                 fprintf(stderr,"UnLock was never called for lock %lx,\n\It was
X created by DupLock at file %s line %d\n",lp->lock,lp->file,lp->line);
X             }
X             else
X             {
X                 fprintf(stderr,"UnLock was never called for lock %lx,\n\It was
X created by a Lock of %s\nat file %s line %d\n",lp->lock,lp->name,lp->file,lp->l
Xine);
X             }
X         }
X         if (lp->name != NULL)
X             FreeMem(lp->name,(long)(strlen(lp->name)+1));
X         freep = lp;
X         lp = lp->next;
X         FreeMem(freep,(long)sizeof(struct TrackingLockData));
X    }
X    printf("Total tracked Lock and DupLock calls %ld, UnLock calls %ld\n",Track
XerLockCount,TrackerUnLockCount);
X}
X
Xvoid TrackerExitReport()
X{
X    ReportUnfreedMemory();
X    ReportUnfreedLocks();
X    ReportUnfreedSignals();
X}
SHAR_EOF
echo "extracting tracker.h"
sed 's/^X//' << \SHAR_EOF > tracker.h
X/* Resource tracking routines, by Karl Lehenbauer
X   Lattice version by David Gay.
X   This code is in the public domain
X*/
X
X#ifdef DEBUG
X/* tracking macros to use tracker routines */
X
X#define AllocMem(x,y) TrackingAllocMem((x),(y),__FILE__,__LINE__)
X#define FreeMem(x,y) TrackingFreeMem((x),(y),__FILE__,__LINE__)
X
X#define AllocSignal(x) TrackingAllocSignal((x),__FILE__,__LINE__)
X#define FreeSignal(x) TrackingFreeSignal((x),__FILE__,__LINE__);
X
X#define Lock(x,y) TheTrackingLock((x),(y),__FILE__,__LINE__);
X#define UnLock(x) TheTrackingUnLock((x),__FILE__,__LINE__);
X#define DupLock(x) TheTrackingDupLock((x),__FILE__,__LINE__);
X#define ParentDir(x) TheTrackingParentDir((x),__FILE__,__LINE__);
X
Xvoid *TrackingAllocMem(long amount, long flags, char *file, int line);
Xvoid TrackingFreeMem(void *where, long amount, char *file, int line);
X
Xlong TrackingAllocSignal(long signal_num, char *file, int line);
Xvoid TrackingFreeSignal(long signal_num, char *file, int line);
X
Xvoid *TrackingAllocRaster();
Xvoid TrackingFreeRaster();
X
XBPTR TheTrackingLock(char *name, long accessMode, char *file, int line);
XBPTR TheTrackingDupLock(BPTR lock, char *file, int line);
Xvoid TheTrackingUnLock(BPTR lock, char *file, int line);
XBPTR TheTrackingParentDir(BPTR lock, char *file, int line);
X
Xvoid TrackerExitReport(void);
X
X#endif
X
SHAR_EOF
echo "extracting tracker.readme"
sed 's/^X//' << \SHAR_EOF > tracker.readme
X[even though in an alpha state, I think this will be of great use to
X Aztec C programmers who want to be sure they're freeing all of their
X memory -karl]
X
X
XC Programmer's Amiga Resource Tracking Routines    Version 0.0a   1/5/89
X-------------------------------------------------------------------------
X
XThis code and documentation is released to the Public Domain without any
Xrestrictions on use, resale or redistribution.
X
XNo license or warranty of appropriateness, usefulness or bug-freeness is
Xexpressed or implied.   This is free code.  We don't have a contract.
X
XWritten by:
X
XKarl Lehenbauer, Hackercorp, 3918 Panorama, Missouri City, TX, USA  77459
X(713) 438-4964 voice,  (713) 438-5018 data
XUsenet: uunet!sugar!karl, Internet: karl@sugar.uu.net, BIX: kelehen
X
X
XThese routines were written to aid C programmers in insuring that their
Xprograms are  properly returning all the memory, signals and locks they
Xhave allocated.
X
XTo use them, include tracker.h in your C source programs and recompile.
X(The use of an "includes.c" along with the Manx +i and +h flags to
Xprecompile the symbol table obviates the necessity of editing
X'#include "tracker.h"' into every one of your source files.)
XNext, edit your exit routine to call TrackerExitReport() after it has
Xfreed everything.  Then, compile tracker.c using the memory model you're
Xusing for the rest of your code and link your program to include tracker.o.
X(This can all be done in your makefile if you've got it set up right.)
XFinally, run your program.
X
XThe program must either be initiated from the CLI or you must edit your
Xprogram's startup code to fopen stderr and direct it somewhere  (like
Xto a  window or a file) or you won't get any of the resource tracker's
Xmessages, or worse.
X
XAs your program runs, every time it allocs memory via AllocMem(), allocs
Xa signal via AllocSignal() or gets a Lock via Lock(), special tracking
Xroutines execute instead (thanks to some macros defined by tracker.h)
Xwhich, in addition to performing the action you requested, record
Xinformation about what you requested and what you got.  For AllocMem(),
Xthe source file and line of the AllocMem call as well as the amount of memory
Xrequested and the pointer to the memory returned are recorded.  For
XAllocSignal(), only the signal numbers allocated are recorded at this time.
XFor Lock(), the file name to be locked, source file and line number and
Xthe lock returned are recorded.
X
XWhen your program frees memory via FreeMem(), a special tracking version
Xof FreeMem is executed that searches the list of entries recorded by the
Xtracking version of AllocMem().  The resource tracker reports if you free
Xsomething more than once, if you free something that you didn't allocate
Xor if the length that you are freeing differes from what you allocated.
XThis includes the source file name and line number of the matching AllocMem
X(when it is known) and always includes the source file and line for FreeMem.
X
XWhen your program frees a signal via FreeSignal(), a tracking version
Xof FreeSignal checks to see if you have allocated the signal you are
Xnow freeing.  If you haven't, it reports it, but it doesn't include the
Xfile name and line number at this time.  I don't think this is a serious
Xproblem, as signals aren't as critical as the other stuff, but I may add
Xit in a future version.
X
XWhen your program unlocks a lock via UnLock(), a tracking version of UnLock
Xsearches the list of recorded locks to see if you locked the lock you are
Xunlocking and report accordingly.
X
XThe tracker exit report provided by TrackerExitReport() is where most of
Xthe bugs are identified.  TrackerExitReport identifies all AllocMems that
Xdidn't have a corresponding FreeMem, including the source file and line
Xof the call to AllocMem as well as the address and size of the memory
Xin question.  The resource tracker does not free the memory for you because
Xyou may have not freed the memory on purpose (for example, you may have
Xspawned a task that uses it will free it later) and it cannot know that.
X
XThe exit report details all signals that weren't freed.  This isn't very
Ximportant, in my opinion.
X
XAlso, the exit report prints information on all file locks that were made
Xthat didn't have a corresponding UnLock.  This information includes the
Xname of the file, value of the lock and the source file and line of the
Xcode that locked it.
X
XThe exit report also prints the number of calls to allocate and free memory,
Xallocate and free signals and to lock and unlock files as a gross indicator
Xof whether you're cleaning everything up properly.
X
XNote that, in the default configuration, memory that is freed and
Xreallocated will screw up the tracker because the tracker continues
Xto track memory objects after they have been  freed. This is a tradeoff
Xbetween being to be able to detect multiple frees of the same memory or
Xnot.  If that's a problem, tracker.c can be recompiled with a
X-DFORGET_MEMORY_WHEN_FREED option so that it will not try to detect
Xmultiple frees.
X
XThe same is true for the lock tracking routines, although in that case
Xthe argument is more clear that unlocks should cause the lock tracking
Xentry to be discarded, because multiple unlocks are common and multiple
Xlocks and unlocks of the same file  during execution are also conceivably
Xpretty common.  Right now by default, the tracker will track locks after
Xthey have been freed.   To change this behavior, recompile tracker.c with
Xthe -DFORGET_LOCKS_WHEN_UNLOCKED option.
X
XUnfortunately, the tracker macros that redefine AllocMem and such will
Xcause your compiler to barf on any files you have that declare them
Xas external.  If that happens, either remove the external declarations
X(and include <functions.h>) or move them to be before the include of
Xtracker.h.
X
X
XALPHA RELEASE, SOFTWARE STATUS
X------------------------------
X
XThe Lock, Unlock and DupLock tracking routines have not been tested
Xadequately.  The signal stuff works OK, but that's no biggie.  The
Xmain thing of interest is the tracking AllocMem and FreeMem, which
XI have used successfully on several programs that I have been working
Xon.
X
X-karl @ The Hacker's Haven, Houston, TX -- 5-Jan-89
X
XP.S.  Note that TrackerExitReport() must be called to get the tracking
X routines to free the memory they have allocated, so it's a good idea
X to call it from your abnormal exit (_abort, etc) routines as well as
X normal exit.   Also, that's good because you can make sure you're freeing
X properly from your strange abort conditions, a thing that's hard to get
X right.
SHAR_EOF
echo "End of archive 6 (of 7)"
# if you want to concatenate archives, remove anything after this line
exit