#include "os.h"
#include "objects.h"
#include "spaces.h"
#include "paths.h"
#include "regions.h"
#include "fonts.h"
#include "pictures.h"
#include "strokes.h"
#include "trig.h"
struct segment *
CopyPath(struct segment *p0)
{
register struct segment *p,*n = NULL,*last = NULL,*anchor;
for (p = p0, anchor = NULL; p != NULL; p = p->link) {
ARGCHECK((!ISPATHTYPE(p->type) || (p != p0 && p->last != NULL)),
"CopyPath: invalid segment", p, NULL, (0), struct segment *);
if (p->type == TEXTTYPE)
n = (struct segment *) CopyText(p);
else
n = (struct segment *)Allocate(p->size, p, 0);
n->last = NULL;
if (anchor == NULL)
anchor = n;
else
last->link = n;
last = n;
}
if (anchor != NULL) {
n->link = NULL;
anchor->last = n;
}
return(anchor);
}
void
KillPath(struct segment *p)
{
register struct segment *linkp;
if ( (--(p->references) > 1) ||
( (p->references == 1) && !ISPERMANENT(p->flag) ) )
return;
while (p != NULL) {
if (!ISPATHTYPE(p->type)) {
ArgErr("KillPath: bad segment", p, NULL);
return;
}
linkp = p->link;
if (p->type == TEXTTYPE)
KillText(p);
else
Free(p);
p = linkp;
}
}
static struct segment movetemplate = { MOVETYPE, 0, 1, sizeof(struct segment), 0,
NULL, NULL, {0, 0} };
struct segment *
t1_Loc(struct XYspace *S,
double x, double y)
{
register struct segment *r;
r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
TYPECHECK("Loc", S, SPACETYPE, r, (0), struct segment *);
r->last = r;
r->context = S->context;
(*S->convert)(&r->dest, S, x, y);
ConsumeSpace(S);
return(r);
}
struct segment *
ILoc(struct XYspace *S,
int x, int y)
{
register struct segment *r;
r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
TYPECHECK("Loc", S, SPACETYPE, r, (0), struct segment *);
r->last = r;
r->context = S->context;
(*S->iconvert)(&r->dest, S, (long) x, (long) y);
ConsumeSpace(S);
return(r);
}
struct segment *
SubLoc(struct segment *p1, struct segment *p2)
{
ARGCHECK(!ISLOCATION(p1), "SubLoc: bad first arg", p1, NULL, (0), struct segment *);
ARGCHECK(!ISLOCATION(p2), "SubLoc: bad second arg", p2, NULL, (0), struct segment *);
p1 = UniquePath(p1);
p1->dest.x -= p2->dest.x;
p1->dest.y -= p2->dest.y;
ConsumePath(p2);
return(p1);
}
struct segment *
t1_PathSegment(int type,
fractpel x, fractpel y)
{
register struct segment *r;
r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
r->type = type;
r->last = r;
r->dest.x = x;
r->dest.y = y;
return(r);
}
struct segment *
Line(struct segment *P)
{
ARGCHECK(!ISLOCATION(P), "Line: arg not a location", P, NULL, (0), struct segment *);
P = UniquePath(P);
P->type = LINETYPE;
return(P);
}
struct beziersegment *
Bezier(struct segment *B,
struct segment *C,
struct segment *D)
{
static struct beziersegment template =
{ BEZIERTYPE, 0, 1, sizeof(struct beziersegment), 0,
NULL, NULL, { 0, 0 }, { 0, 0 }, { 0, 0 } };
register struct beziersegment *r;
ARGCHECK(!ISLOCATION(B), "Bezier: bad B", B, NULL, (2,C,D), struct beziersegment *);
ARGCHECK(!ISLOCATION(C), "Bezier: bad C", C, NULL, (2,B,D), struct beziersegment *);
ARGCHECK(!ISLOCATION(D), "Bezier: bad D", D, NULL, (2,B,C), struct beziersegment *);
r = (struct beziersegment *)Allocate(sizeof(struct beziersegment), &template, 0);
r->last = (struct segment *) r;
r->dest.x = D->dest.x;
r->dest.y = D->dest.y;
r->B.x = B->dest.x;
r->B.y = B->dest.y;
r->C.x = C->dest.x;
r->C.y = C->dest.y;
ConsumePath(B);
ConsumePath(C);
ConsumePath(D);
return(r);
}
struct hintsegment *
Hint(struct XYspace *S, float ref, float width,
char orientation, char hinttype, char adjusttype, char direction,
int label)
{
static struct hintsegment template = { HINTTYPE, 0, 1, sizeof(struct hintsegment), 0,
NULL, NULL, { 0, 0 }, { 0, 0 }, { 0, 0 },
' ', ' ', ' ', ' ', 0};
register struct hintsegment *r;
r = (struct hintsegment *)Allocate(sizeof(struct hintsegment), &template, 0);
r->orientation = orientation;
if (width == 0.0) width = 1.0;
if (orientation == 'h') {
(*S->convert)(&r->ref, S, 0.0, ref);
(*S->convert)(&r->width, S, 0.0, width);
}
else if (orientation == 'v') {
(*S->convert)(&r->ref, S, ref, 0.0);
(*S->convert)(&r->width, S, width, 0.0);
}
else
return((struct hintsegment *)ArgErr("Hint: orient not 'h' or 'v'", NULL, NULL));
if (r->width.x < 0) r->width.x = - r->width.x;
if (r->width.y < 0) r->width.y = - r->width.y;
r->hinttype = hinttype;
r->adjusttype = adjusttype;
r->direction = direction;
r->label = label;
r->last = (struct segment *) r;
ConsumeSpace(S);
return(r);
}
#define POP(p) \
{ register struct segment *linkp; \
linkp = p->link; \
if (linkp != NULL) \
linkp->last = p->last; \
Free(p); \
p = linkp; }
#define INSERT(b,p,a) b->link=p; p->link=a; p->last=NULL
struct segment *
Join(struct segment *p1, struct segment *p2)
{
if (p2 != NULL) {
if (!ISPATHTYPE(p2->type)) {
if (p1 == NULL)
return((struct segment *)Unique(p2));
switch (p1->type) {
case REGIONTYPE:
case STROKEPATHTYPE:
p1 = CoercePath(p1);
break;
default:
return((struct segment *)BegHandle(p1, p2));
}
}
ARGCHECK((p2->last == NULL), "Join: right arg not anchor", p2, NULL, (1,p1), struct segment *);
p2 = UniquePath(p2);
if (p2->type == TEXTTYPE || p2->type == MOVETYPE) {
if (p1 == NULL)
return(p2);
if (ISLOCATION(p1)) {
p2->dest.x += p1->dest.x;
p2->dest.y += p1->dest.y;
ConsumePath(p1);
return(p2);
}
}
}
else
return((struct segment *)Unique(p1));
if (p1 != NULL) {
if (!ISPATHTYPE(p1->type))
switch (p2->type) {
case REGIONTYPE:
case STROKEPATHTYPE:
p2 = CoercePath(p2);
break;
default:
return((struct segment *)EndHandle(p1, p2));
}
ARGCHECK((p1->last == NULL), "Join: left arg not anchor", p1, NULL, (1,p2), struct segment *);
p1 = UniquePath(p1);
}
else
return(p2);
if (p1->last->type == MOVETYPE && p2->type == MOVETYPE) {
p1->last->flag |= p2->flag;
p1->last->dest.x += p2->dest.x;
p1->last->dest.y += p2->dest.y;
POP(p2);
if (p2 == NULL)
return(p1);
}
if (p1->type == TEXTTYPE) {
if (p2->type != TEXTTYPE && !ISLOCATION(p2))
p1 = CoerceText(p1);
}
else {
if (p2->type == TEXTTYPE) {
if (ISLOCATION(p1)) {
p2->dest.x += p1->dest.x;
p2->dest.y += p1->dest.y;
Free(p1);
return(p2);
}
else
p2 = CoerceText(p2);
}
}
CONCAT(p1, p2);
return(p1);
}
struct segment *
t1_JoinSegment(struct segment *before,
int type,
fractpel x, fractpel y,
struct segment *after)
{
register struct segment *r;
r = PathSegment(type, x, y);
if (before != NULL) {
CONCAT(before, r);
r = before;
}
else
r->context = after->context;
if (after != NULL)
CONCAT(r, after);
return(r);
}
struct segment *
t1_ClosePath(struct segment *p0,
int lastonly)
{
register struct segment *p,*last = NULL,*start;
register fractpel x,y;
register fractpel firstx = 0,firsty = 0;
register struct segment *lastnonhint = NULL;
if (p0 != NULL && p0->type == TEXTTYPE)
return(UniquePath(p0));
if (p0->type == STROKEPATHTYPE)
return((struct segment *)Unique(p0));
if (p0 == NULL || p0->type != MOVETYPE)
p0 = JoinSegment(NULL, MOVETYPE, 0, 0, p0);
TYPECHECK("ClosePath", p0, MOVETYPE, NULL, (0), struct segment *);
if (p0->last->type != MOVETYPE)
p0 = JoinSegment(p0, MOVETYPE, 0, 0, NULL);
p0 = UniquePath(p0);
for (p = p0, x = y = 0, start = NULL;
p != NULL;
x += p->dest.x, y += p->dest.y, last = p, p = p->link)
{
if (p->type == MOVETYPE) {
if (start != NULL && (lastonly?p->link==NULL:TRUE) &&
!(ISCLOSED(start->flag) && LASTCLOSED(last->flag))) {
register struct segment *r;
start->flag |= ISCLOSED(ON);
r = PathSegment(LINETYPE, firstx - x,
firsty - y);
INSERT(last, r, p);
r->flag |= LASTCLOSED(ON);
{
#define CLOSEFUDGE 3
if (r->dest.x != 0 || r->dest.y != 0) {
if (r->dest.x <= CLOSEFUDGE && r->dest.x >= -CLOSEFUDGE
&& r->dest.y <= CLOSEFUDGE && r->dest.y >= -CLOSEFUDGE) {
lastnonhint->dest.x += r->dest.x;
lastnonhint->dest.y += r->dest.y;
r->dest.x = r->dest.y = 0;
}
}
}
if (p->link != NULL) {
p->dest.x += x - firstx;
p->dest.y += y - firsty;
x = firstx;
y = firsty;
}
}
start = p;
firstx = x + p->dest.x;
firsty = y + p->dest.y;
}
else if (p->type != HINTTYPE)
lastnonhint = p;
}
return(p0);
}
static struct segment *SplitPath ( struct segment *anchor,
struct segment *before );
static struct segment *DropSubPath ( struct segment *p0 );
static struct segment *ReverseSubPath ( struct segment *p );
struct segment *
Reverse(struct segment *p)
{
register struct segment *r;
register struct segment *nextp;
if (p == NULL)
return(NULL);
ARGCHECK(!ISPATHANCHOR(p), "Reverse: invalid path", p, NULL, (0), struct segment *);
if (p->type == TEXTTYPE)
p = CoerceText(p);
p = UniquePath(p);
r = NULL;
do {
nextp = DropSubPath(p);
p = ReverseSubPath(p);
r = Join(p, r);
p = nextp;
} while (p != NULL);
return(r);
}
static struct segment *
ReverseSubPath(struct segment *p)
{
register struct segment *r;
register struct segment *nextp;
register int wasclosed;
if (p == NULL)
return(NULL);
wasclosed = ISCLOSED(p->flag);
r = NULL;
do {
p->dest.x = - p->dest.x; p->dest.y = - p->dest.y;
p->flag &= ~(ISCLOSED(ON) | LASTCLOSED(ON));
switch (p->type) {
case LINETYPE:
case MOVETYPE:
break;
case CONICTYPE:
{
register struct conicsegment *cp = (struct conicsegment *) p;
cp->M.x += cp->dest.x; cp->M.y += cp->dest.y;
}
break;
case BEZIERTYPE:
{
register struct beziersegment *bp = (struct beziersegment *) p;
bp->B.x += bp->dest.x; bp->B.y += bp->dest.y;
bp->C.x += bp->dest.x; bp->C.y += bp->dest.y;
}
break;
case HINTTYPE:
{
register struct hintsegment *hp = (struct hintsegment *) p;
hp->ref.x = -hp->ref.x; hp->ref.y = -hp->ref.y;
}
break;
default:
Abort("Reverse: bad path segment");
}
nextp = p->link;
p->link = NULL;
p->last = p;
if (r != NULL)
CONCAT(p,r);
r = p;
p = nextp;
} while (p != NULL);
if (wasclosed)
r = ClosePath(r);
return(r);
}
static struct segment *
DropSubPath(struct segment *p0)
{
register struct segment *p;
for (p = p0; p->link != NULL; p = p->link) {
if (p->link->type == MOVETYPE)
break;
}
return(SplitPath(p0, p));
}
static struct segment *
SplitPath(struct segment *anchor, struct segment *before)
{
register struct segment *r;
if (before == anchor->last)
return(NULL);
r = before->link;
r->last = anchor->last;
anchor->last = before;
before->link = NULL;
return(r);
}
static void
UnClose(struct segment *p0)
{
register struct segment *p;
for (p=p0; p->link->link != NULL; p=p->link) { ; }
if (!LASTCLOSED(p->link->flag))
Abort("UnClose: no LASTCLOSED");
Free(SplitPath(p0, p));
p0->flag &= ~ISCLOSED(ON);
}
struct segment *
ReverseSubPaths(struct segment *p)
{
register struct segment *r;
register struct segment *nextp;
int wasclosed;
register struct segment *nomove;
struct fractpoint delta;
if (p == NULL)
return(NULL);
ARGCHECK(!ISPATHANCHOR(p), "ReverseSubPaths: invalid path", p, NULL, (0), struct segment *);
if (p->type == TEXTTYPE)
p = CoerceText(p);
if (p->type != MOVETYPE)
p = JoinSegment(NULL, MOVETYPE, 0, 0, p);
p = UniquePath(p);
r = NULL;
for (; p != NULL;) {
nextp = DropSubPath(p);
wasclosed = ISCLOSED(p->flag);
if (wasclosed)
UnClose(p);
nomove = SplitPath(p, p);
r = Join(r, p);
PathDelta(nomove, &delta);
nomove = ReverseSubPath(nomove);
p->dest.x += delta.x;
p->dest.y += delta.y;
if (nextp != NULL) {
nextp->dest.x += delta.x;
nextp->dest.y += delta.y;
}
if (wasclosed) {
nomove = ClosePath(nomove);
nextp->dest.x -= delta.x;
nextp->dest.y -= delta.y;
}
r = Join(r, nomove);
p = nextp;
}
return(r);
}
struct segment *
PathTransform(struct segment *p0,
struct XYspace *S)
{
register struct segment *p;
register fractpel newx,newy;
register fractpel oldx,oldy;
register fractpel savex,savey;
p0 = UniquePath(p0);
newx = newy = oldx = oldy = 0;
for (p=p0; p != NULL; p=p->link) {
savex = p->dest.x; savey = p->dest.y;
(*S->iconvert)(&p->dest, S, p->dest.x + oldx, p->dest.y + oldy);
p->dest.x -= newx;
p->dest.y -= newy;
switch (p->type) {
case LINETYPE:
case MOVETYPE:
break;
case CONICTYPE:
{
register struct conicsegment *cp = (struct conicsegment *) p;
(*S->iconvert)(&cp->M, S, cp->M.x + oldx, cp->M.y + oldy);
cp->M.x -= newx;
cp->M.y -= newy;
break;
}
case BEZIERTYPE:
{
register struct beziersegment *bp = (struct beziersegment *) p;
(*S->iconvert)(&bp->B, S, bp->B.x + oldx, bp->B.y + oldy);
bp->B.x -= newx;
bp->B.y -= newy;
(*S->iconvert)(&bp->C, S, bp->C.x + oldx, bp->C.y + oldy);
bp->C.x -= newx;
bp->C.y -= newy;
break;
}
case HINTTYPE:
{
register struct hintsegment *hp = (struct hintsegment *) p;
(*S->iconvert)(&hp->ref, S, hp->ref.x + oldx, hp->ref.y + oldy);
hp->ref.x -= newx;
hp->ref.y -= newy;
(*S->iconvert)(&hp->width, S, hp->width.x, hp->width.y);
break;
}
case TEXTTYPE:
{
XformText(p,S);
break;
}
default:
Abort("PathTransform: invalid segment");
}
oldx += savex;
oldy += savey;
newx += p->dest.x;
newy += p->dest.y;
}
return(p0);
}
void
PathDelta(struct segment *p,
struct fractpoint *pt)
{
struct fractpoint mypoint;
register fractpel x,y;
for (x=y=0; p != NULL; p=p->link) {
x += p->dest.x;
y += p->dest.y;
if (p->type == TEXTTYPE) {
TextDelta(p, &mypoint);
x += mypoint.x;
y += mypoint.y;
}
}
pt->x = x;
pt->y = y;
}
struct segment *
BoundingBox(pel h, pel w)
{
register struct segment *path;
path = PathSegment(LINETYPE, -TOFRACTPEL(w), 0);
path = JoinSegment(NULL, LINETYPE, 0, -TOFRACTPEL(h), path);
path = JoinSegment(NULL, LINETYPE, TOFRACTPEL(w), 0, path);
path = ClosePath(path);
return(path);
}
void
QueryLoc(struct segment *P,
struct XYspace *S,
double *xP, double *yP)
{
if (!ISLOCATION(P)) {
ArgErr("QueryLoc: first arg not a location", P, NULL);
return;
}
if (S->type != SPACETYPE) {
ArgErr("QueryLoc: second arg not a space", S, NULL);
return;
}
UnConvert(S, &P->dest, xP, yP);
}
void
QueryPath(struct segment *path,
int *typeP,
struct segment **Bp,
struct segment **Cp,
struct segment **Dp,
double *fP)
{
register int coerced = FALSE;
if (path == NULL) {
*typeP = -1;
return;
}
if (!ISPATHANCHOR(path)) {
ArgErr("QueryPath: arg not a valid path", path, NULL);
}
if (path->type == TEXTTYPE) {
path = CoerceText(path);
coerced = TRUE;
}
switch (path->type) {
case MOVETYPE:
*typeP = 0;
*Bp = PathSegment(MOVETYPE, path->dest.x, path->dest.y);
break;
case LINETYPE:
*typeP = (LASTCLOSED(path->flag)) ? 4 : 1;
*Bp = PathSegment(MOVETYPE, path->dest.x, path->dest.y);
break;
case CONICTYPE:
{
register struct conicsegment *cp = (struct conicsegment *) path;
*typeP = 2;
*Bp = PathSegment(MOVETYPE, cp->M.x, cp->M.y);
*Cp = PathSegment(MOVETYPE, cp->dest.x, cp->dest.y);
*fP = cp->roundness;
}
break;
case BEZIERTYPE:
{
register struct beziersegment *bp = (struct beziersegment *) path;
*typeP = 3;
*Bp = PathSegment(MOVETYPE, bp->B.x, bp->B.y);
*Cp = PathSegment(MOVETYPE, bp->C.x, bp->C.y);
*Dp = PathSegment(MOVETYPE, bp->dest.x, bp->dest.y);
}
break;
case HINTTYPE:
*typeP = 5;
break;
default:
Abort("QueryPath: unknown segment");
}
if (coerced)
KillPath(path);
}
void
QueryBounds(struct segment *p0,
struct XYspace *S,
double *xminP,
double *yminP,
double *xmaxP,
double *ymaxP)
{
register struct segment *path;
register fractpel lastx,lasty;
register fractpel x,y;
struct fractpoint min;
struct fractpoint max;
int coerced = FALSE;
double x1,y1,x2,y2,x3,y3,x4,y4;
if (S->type != SPACETYPE) {
ArgErr("QueryBounds: bad XYspace", S, NULL);
return;
}
min.x = min.y = max.x = max.y = 0;
if (p0 != NULL) {
if (!ISPATHANCHOR(p0)) {
switch(p0->type) {
case STROKEPATHTYPE:
p0 = (struct segment *) DoStroke(Dup(p0));
case REGIONTYPE:
p0 = RegionBounds((struct region *)p0);
break;
case PICTURETYPE:
p0 = PictureBounds(p0);
break;
default:
ArgErr("QueryBounds: bad object", p0, NULL);
return;
}
coerced = TRUE;
}
if (p0->type == TEXTTYPE) {
p0 = (struct segment *)CoerceText(Dup(p0));
coerced = TRUE;
}
if (p0->type == MOVETYPE) {
min.x = max.x = p0->dest.x;
min.y = max.y = p0->dest.y;
}
}
lastx = lasty = 0;
for (path = p0; path != NULL; path = path->link) {
x = lastx + path->dest.x;
y = lasty + path->dest.y;
switch (path->type) {
case LINETYPE:
break;
case CONICTYPE:
{
register struct conicsegment *cp = (struct conicsegment *) path;
register fractpel Mx = lastx + cp->M.x;
register fractpel My = lasty + cp->M.y;
register fractpel deltax = 0.5 * cp->roundness * cp->dest.x;
register fractpel deltay = 0.5 * cp->roundness * cp->dest.y;
register fractpel Px = Mx - deltax;
register fractpel Py = My - deltay;
register fractpel Qx = Mx + deltax;
register fractpel Qy = My + deltay;
if (Mx < min.x) min.x = Mx;
else if (Mx > max.x) max.x = Mx;
if (My < min.y) min.y = My;
else if (My > max.y) max.y = My;
if (Px < min.x) min.x = Px;
else if (Px > max.x) max.x = Px;
if (Py < min.y) min.y = Py;
else if (Py > max.y) max.y = Py;
if (Qx < min.x) min.x = Qx;
else if (Qx > max.x) max.x = Qx;
if (Qy < min.y) min.y = Qy;
else if (Qy > max.y) max.y = Qy;
}
break;
case MOVETYPE:
if (path->link == NULL)
goto done;
break;
case BEZIERTYPE:
{
register struct beziersegment *bp = (struct beziersegment *) path;
register fractpel Bx = lastx + bp->B.x;
register fractpel By = lasty + bp->B.y;
register fractpel Cx = lastx + bp->C.x;
register fractpel Cy = lasty + bp->C.y;
if (Bx < min.x) min.x = Bx;
else if (Bx > max.x) max.x = Bx;
if (By < min.y) min.y = By;
else if (By > max.y) max.y = By;
if (Cx < min.x) min.x = Cx;
else if (Cx > max.x) max.x = Cx;
if (Cy < min.y) min.y = Cy;
else if (Cy > max.y) max.y = Cy;
}
break;
case HINTTYPE:
break;
default:
Abort("QueryBounds: unknown type");
}
if (x < min.x) min.x = x;
else if (x > max.x) max.x = x;
if (y < min.y) min.y = y;
else if (y > max.y) max.y = y;
lastx = x; lasty = y;
}
done:
UnConvert(S, &min, &x1, &y1);
UnConvert(S, &max, &x4, &y4);
x = min.x; min.x = max.x; max.x = x;
UnConvert(S, &min, &x2, &y2);
UnConvert(S, &max, &x3, &y3);
*xminP = *xmaxP = x1;
if (x2 < *xminP) *xminP = x2;
else if (x2 > *xmaxP) *xmaxP = x2;
if (x3 < *xminP) *xminP = x3;
else if (x3 > *xmaxP) *xmaxP = x3;
if (x4 < *xminP) *xminP = x4;
else if (x4 > *xmaxP) *xmaxP = x4;
*yminP = *ymaxP = y1;
if (y2 < *yminP) *yminP = y2;
else if (y2 > *ymaxP) *ymaxP = y2;
if (y3 < *yminP) *yminP = y3;
else if (y3 > *ymaxP) *ymaxP = y3;
if (y4 < *yminP) *yminP = y4;
else if (y4 > *ymaxP) *ymaxP = y4;
if (coerced)
Destroy(p0);
}
struct segment *
BoxPath(struct XYspace *S, int h, int w)
{
struct segment *path;
path = Join( Line(ILoc(S, w, 0)), Line(ILoc(S, 0, h)) );
path = JoinSegment(path, LINETYPE, -path->dest.x, -path->dest.y, NULL);
return(ClosePath(path));
}
struct segment *
DropSegment(struct segment *path)
{
if (path != NULL && path->type == STROKEPATHTYPE)
path = CoercePath(path);
ARGCHECK((path == NULL || !ISPATHANCHOR(path)),
"DropSegment: arg not a non-null path", path, path, (0), struct segment *);
if (path->type == TEXTTYPE)
path = CoerceText(path);
path = UniquePath(path);
POP(path);
return(path);
}
struct segment *
HeadSegment(struct segment *path)
{
if (path == NULL)
return(NULL);
if (path->type == STROKEPATHTYPE)
path = CoercePath(path);
ARGCHECK(!ISPATHANCHOR(path), "HeadSegment: arg not a path", path, path, (0), struct segment *);
if (path->type == TEXTTYPE)
path = CoerceText(path);
path = UniquePath(path);
if (path->link != NULL)
KillPath(path->link);
path->link = NULL;
path->last = path;
return(path);
}
void
DumpPath(struct segment *p)
{
}