PCB part 7 of 10
Andreas Nowatzyk
agn at cmu-cs-unh.ARPA
Mon Aug 12 09:59:38 AEST 1985
#
# type sh /usru0/agn/pcb/distr/../usenet/part7 to unpack this archive.
#
echo extracting pmnrt.c...
cat >pmnrt.c <<'!E!O!F!'
/***************************************************************\
* *
* PCB program *
* *
* Manual routing functions *
* *
* (c) 1985 A. Nowatzyk *
* *
\***************************************************************/
#include "pparm.h"
#include "pcdst.h"
#define maxtrc 200 /* number of items in trace buf */
#define sactrc 10 /* items lost on overflow */
extern double atan2();
extern struct nlhd *choose(), *get_net();
static plow_f = 0; /* plow flag */
static int trc_cnt = 0; /* trace counter */
static struct trace { /* trace buffer (for delete last)*/
int x, y; /* coordinate */
int ty; /* type (hole, side) */
} trc_buf[maxtrc];
static int sof_seg = 0; /* color of first segment */
extern int sp[9][2]; /* search pattern */
/*ARGSUSED*/
net_sel (ctx) /* select a net for routing */
int ctx;
{
struct nlhd *t;
if (!placed) { /* routing only after placement */
if (V.cnet)
deseln (V.cnet); /* should not happen */
if (ck_placed ())
placed = 1; /* everything is placed */
else
return PLACE;
}
if (V.cnet) { /* we are within a net */
if (!(V.cnet -> f))
return (START); /* continue on selected net */
else
deseln (V.cnet);
}
while (t = choose ()) {/* found a net to work on */
if (!selnet (t)) {
adjw (V.cnet);
err_msg (t -> name);
return (START);
}
deseln (t); /* skip complete nets */
}
return (EDIT); /* no nets found */
}
net_desel (ctx) /* deselect a net */
int ctx;
/******************************************************************\
* *
* Eficiency hack: to avoid a deselec/select cycle for incomplete *
* nets, those net remain selected if the next context is START. *
* *
\******************************************************************/
{ int i;
static int svcnt = 0; /* auto save counter */
plow_f = 0;
if (ctx == ROUTE || ctx == PLOW) /* don't deselect to enter R-cntx*/
return;
if (V.cnet) {
i = cchk (V.cnet); /* update net state */
nettgo -= i ^ V.cnet -> f;
V.cnet -> f = i;
nets_msg (nettgo);
if (V.cnet -> f) { /* completed net ? */
if (++svcnt > savcnt) { /* time to auto save ? */
svcnt = 0;
Ferr_msg ("Busy: saving work");
save (0);
beep ();
err_msg ("Done!");
}
}
if (V.cnet -> f || ctx != START) {
deseln (V.cnet); /* time to drop the net */
}
}
}
/*ARGSUSED*/
start_nt (x, y, ctx) /* start a copper trace */
int x, y, ctx;
/*****************************************************************\
* *
* This function tries to find a start point for a copper trace: *
* 1. if the cursor is near a hole and the hole is part of an *
* unconnected net, the hole is selected as a start. *
* 2. if the cursor is near a trace of an unfinished net, the *
* nearest point of that trace is used as an start point. *
* this might fail due to lack of space for a via hole. *
* 3. If everything fails, the preview map is displayed. *
* *
\*****************************************************************/
{
struct nlhd *p, *loc (), *fly ();
char buf[40 + nmmax];
sof_seg = 0; /* reset first color */
if (plow_f && trc_buf[0].x == x && trc_buf[0].y == y &&
!(pcb[y][x] & ahb)) {
plow_f = 0;
plow_ini (PLOW);
return plow_src (x, y, PLOW);
}
if (V.cnet -> f) /* deselect a complted net */
deseln (V.cnet);
p = loc (&x, &y); /* try start from hole */
if (!p)
p = fly (&x, &y); /* failed, try a wire */
if (!V.cnet) { /* no net */
if (p && !(p -> f))
selnet (p);
else if (!p) { /* p != 0 is taken care of later */
plow_f = 0;
return (net_sel (0));
}
}
if (!p) { /* start failed */
plow_f = 0;
prev (V.cnet, x, y); /* display preview map */
return (START);}
else if (p -> f) { /* net is already done */
plow_f = 1;
trc_buf[0].x = x;
trc_buf[0].y = y;
sprintf (buf, "Net (%s): done!", p -> name);
err_msg (buf);
if (V.cnet)
deseln (V.cnet);
selnet (p);
return (START);}
else { /* start ok: save start point */
plow_f = 0;
err_msg (p -> name);
trc_buf[0].x = x;
trc_buf[0].y = y;
trc_buf[0].ty = (pcb[y][x] & ahb) ? vec :
((~pcb[y][x] & vec) ? pcb[y][x] & vec : s1b);
trc_cnt = 1;
}
if (p != V.cnet) { /* started on unselected net */
deseln (V.cnet); /* drop current net */
if (selnet (p)) { /* trouble: net is done */
deseln (p);
return (net_sel (0));
}
}
return (ROUTE);
}
struct nlhd *fly (x, y) /* try flying start */
int *x, *y;
{
int i;
for (i = 0; i < 9; ++i) /* look for a selected wire */
if ((pcb[*y + sp[i][1]][*x + sp[i][0]] & vec) &&
(pcb[*y + sp[i][1]][*x + sp[i][0]] & selb)) {
*x += sp[i][0];
*y += sp[i][1];
return (V.cnet);
}
for (i = 0; i < 9; ++i) /* look for any wire */
if (pcb[*y + sp[i][1]][*x + sp[i][0]] & vec) {
*x += sp[i][0];
*y += sp[i][1];
if (~pcb[*y][*x] & vec)
return get_net (*x, *y, vec);
else
return get_net (*x, *y, top_side);
}
return (nil); /* not close to wire */
}
struct nlhd *loc(x, y) /* locate net for given pin */
int *x, *y;
{
int i, j;
for (i = *x - 1; i < *x + 1; ++i)/* look for holes */
for (j = *y - 1; j < *y + 1; ++j)
if (center (&i, &j)) { /* center coordinate */
*x = i;
*y = j;
return get_net(i, j, vec);
}
return (nil); /* no hole found */
}
/*ARGSUSED*/
cont_s1 (x, y, ctx) /* continue a wire trace */
int x, y, ctx;
{
return (cont_nt (x, y, s1b));
}
/*ARGSUSED*/
cont_s2 (x, y, ctx) /* continue a wire trace */
int x, y, ctx;
{
return (cont_nt (x, y, s2b));
}
cont_nt (x, y, sd) /* continue a wire trace */
int x, y, sd;
{ int i;
if ((trc_buf[trc_cnt - 1].x == x) && (trc_buf[trc_cnt - 1].y == y)) {
if (trc_cnt == 1 && !(pcb[y][x] & ahb)) {
trc_cnt = 0;
plow_ini (PLOW);
return plow_src (x, y, PLOW);
}
return (ROUTE); /* ignore 0-length wires */
}
if (trc_cnt == 2)
sof_seg = trc_buf[1].ty;
if (trc_cnt > maxtrc - 3) { /* sufficient space in buffer */
for (i = sactrc; i < trc_cnt; i++) {
trc_buf[i - sactrc].x = trc_buf[i].x;
trc_buf[i - sactrc].y = trc_buf[i].y;
trc_buf[i - sactrc].ty = trc_buf[i].ty;
}
trc_cnt -= sactrc;
}
if (home (x, y, trc_buf[trc_cnt - 1].x, trc_buf[trc_cnt - 1].y,
trc_buf[0].x, trc_buf[0].y, sd, trc_buf[trc_cnt - 1].ty))
return (START); /* successfull home run */
if (sd & ~trc_buf[trc_cnt - 1].ty) {/* need to change sides */
if (viaalg (sd)) {
err_msg ("No space for a hole");
beep ();
return (ROUTE);
}
trc_buf[trc_cnt].ty = sd | ishb;}
else
trc_buf[trc_cnt].ty = sd;
if (pnt (x, y, sd)) /* add a vector to wire */
return (START);
return (ROUTE);
}
pnt (x_cur, y_cur, si) /* paint a wire segment */
int x_cur, y_cur, si;
{
int i,
j,
k,
l, /* random scratch */
x,
y,
x1,
y1, /* used for test scan */
xo, yo,
dx,
dy,
dxd,
dyd; /* direction vectors */
xo = trc_buf[trc_cnt - 1].x;/* get origin */
yo = trc_buf[trc_cnt - 1].y;
i = (atan2 ((y_cur - yo) * 0.5, (x_cur - xo) * 0.5) + 6.676885) * 1.27324;
i = i % 8; /* chose a direction */
dx = dr[i][0];
dy = dr[i][1];
color (si | selb, si | selb);
j = abs (y_cur - yo);
k = abs (x_cur - xo);
if (i & 1)
j = (j < k) ? j : k; /* run min. length (diag.) */
else
j = dx ? k : j; /* run axis projection */
x = xo + dx;
y = yo + dy;
if (i & 1) { /* diagonal check */
dxd = (dx - dy) / 2;
dyd = (dx + dy) / 2;
for (k = 0; k < j; ++k) {/* check for illegal areas */
l = pchk1 (x - dy, y + dx, si, &x1, &y1);
if (l & 1)
break;
l |= pchk1 (x + dy, y - dx, si, &x1, &y1);
if (l & 1)
break;
if (!k && l && !(pcb[y1][x1] & ahb))
/* something special */
l = 0;
l |= pchk1 (x + dx, y + dy, si, &x1, &y1);
if (l & 1)
break;
l |= pchk1 (x + dxd, y + dyd, si, &x1, &y1);
if (l & 1)
break;
l |= pchk1 (x + dyd, y - dxd, si, &x1, &y1);
if (l & 1)
break;
if (l & 2) {
l = pchk2 (x, y, x1, y1, i, si);
if (l)
break;
}
x += dx;
y += dy;
}
}
else { /* horizontal / vertical check */
for (k = 0; k < j; ++k) {/* check for illegal areas */
l = pchk1 (x + dx - dy, y + dy + dx, si, &x1, &y1);
if (l & 1)
break;
l |= pchk1 (x + dx + dy, y + dy - dx, si, &x1, &y1);
if (l & 1)
break;
if (!k && l && !(pcb[y1][x1] & ahb))
/* something special */
l = 0;
l |= pchk1 (x + dx, y + dy, si, &x1, &y1);
if (l & 1)
break;
if (l & 2) {
l = pchk2 (x, y, x1, y1, i, si);
if (l)
break;
}
x += dx;
y += dy;
}
}
if (l == 2) /* an end connection was made */
return (1); /* signal success */
else {
x -= dx; /* correct length */
y -= dy;
plt (xo, yo, x, y); /* plot new segment */
trc_buf[trc_cnt].x = x;
trc_buf[trc_cnt++].y = y;
return (0);
}
}
pchk1 (x, y, b, xs, ys) /* paint check */
int x, y, b, *xs, *ys;
{
int i;
/* printf("pchk1: x=%d y=%d b=%2x xs=%d ys=%d\n",x,y,b,*xs,*ys); */
i = pcb[y][x];
if (!(i & selb)) /* not selected: peace of cake */
return (0 != (i & (fb | b | ahb)));/* touches other net? */
*xs = x;
*ys = y;
return (2); /* needs more careful examination */
}
pchk2 (xe, ye, xt, yt, a, b)/* paint check (the same net) */
int xe, ye, xt, yt, a, b;
{
register int i;
i = pcb[yt][xt];
if (i & b) { /* touches net on same side: ok */
if (!(pcb[ye][xe] & b) &&
!(~pcb[ye + dr[a][1]][xe + dr[a][0]] & (selb | b))) {
xe += dr[a][0]; /* fill diagonal approach gap */
ye += dr[a][1];
}
plt (trc_buf[trc_cnt - 1].x, trc_buf[trc_cnt - 1].y, xe, ye);
return (2);
}
if (i & ahb) /* hole approach */
return (pnt_ha (xe, ye, xt, yt, a, b));
else /* remaining case: touches same net on other side*/
return (pnt_eha (xe, ye, xt, yt, a, b));
}
pnt_ha (xe, ye, xt, yt, a, b)
int xe, ye, xt, yt, a, b;
/********************************************************************\
* *
* paint wire: hole approach - a via hole for the same net is found *
* in the paint - scan. A connection between the new wire and *
* the hole need to be made (this is allways possible). <xe,ye> is *
* the end point of the new wire on side <b> with direction <a>. *
* <xt,yt> is part of the hole (not necessarily the center). *
* <e> and <t> are one unit apart. *
* *
* a 2 is returned if a connection is made. 0 is returned if the *
* hole turns out to be a piece of guard area (pseudo hole). *
* *
\********************************************************************/
{
register int i;
if (!center (&xt, &yt)) /* center on hole */
return 0;
if ((xt == xe + 2 * dr[a][0]) && (yt == ye + 2 * dr[a][1])) {
plt (trc_buf[trc_cnt - 1].x, trc_buf[trc_cnt - 1].y,
xe + 2 * dr[a][0], ye + 2 * dr[a][1]);
return (2); /* hole was staight ahead */
}
if (a & 1) { /* diagonal approach */
if ((abs (xe - xt) >= 2) && (abs (ye - yt)) &&
(pcb[ye - yt][xe - dr[a][0]] & b)) {
/* this is a wired case */
if (abs (yt - (ye - dr[a][1])) > 2)
plt (xt, yt, xt, yt - 2 * dr[a][1]);
else
plt (xt, yt, xt - 2 * dr[a][0], yt);
plt (trc_buf[trc_cnt - 1].x, trc_buf[trc_cnt - 1].y,
xe -= dr[a][0], ye -= dr[a][1]);
ckgp (xe, ye, b);
return (2);
}
xe += dr[a][0];
ye += dr[a][1];
for (i = 0; i < 3; ++i) {
if ((yt == ye) || (xt == xe)) {
plt (xt, yt, xe, ye);
plt (trc_buf[trc_cnt - 1].x, trc_buf[trc_cnt - 1].y, xe, ye);
ckgp (xe, ye, b);
return (2);
}
xe -= dr[a][0];
ye -= dr[a][1];
}
err ("pchk2: funny error - please save the details", xe, ye, xt, yt);}
else {
if ((abs (xt - xe) > 1) && (abs (yt - ye) > 1))
plt (xt, yt, xe, ye);
else {
pxl (xt, yt);
xe += dr[a][0];
ye += dr[a][1];
}
plt (trc_buf[trc_cnt - 1].x, trc_buf[trc_cnt - 1].y, xe, ye);
ckgp (xe, ye, b);
return (2);
}
return 0; /* to keep lint happy */
}
pnt_eha (xe, ye, xt, yt, de, b)
int xe, ye, xt, yt, de, b;
/**********************************************************************\
* *
* paint: end hole allocator - tries to allocate an via-hole. *
* During the paint-wire scan, the new wire finds itself *
* approaching the same net on a the other pcb-side. A via-hole *
* need to be allocated to connect these wires. *
* <xe,ye> is the end point of the new wire, drawn in direction <de>. *
* <xt,yt> is the offending point of the same (selected) net on *
* the other side. <e> and <t> are exactly one unit apart. The *
* new wire is drawn on side <b>. *
* *
* On success, a 2 is returned. Unsuccesfull attemps return a 1. *
* *
\**********************************************************************/
{
int i, j, x, y, dt;
static struct hsp { /* hole search pattern */
int r[7]; /* relative test point position
-1: no displacement = xe,ye */
int h[7]; /* help-point: 0=none -1= xe,ye */
} hasp[9] = {
{ /* diag: xt,yt on +2 */
{ 2, -1, 1, 3, 0, 4, 5},
{ 0, 0, 0, 0, 1, 3, -1} },
{ /* diag: xt,yt on +1 */
{ 1, -1, 0, 7, 4, 3, 5},
{ 0, 0, 0, 0, -1, 0, -1} },
{ /* diag: xt,yt on 0 */
{ 0, -1, 1, 7, 4, 3, 7},
{ 0, 0, 0, 0, -1, -1, -1} },
{ /* diag: xt,yt on -1 */
{ 7, -1, 0, 1, 4, 5, 3},
{ 0, 0, 0, 0, -1, 0, -1} },
{ /* diag: xt,yt on -2 */
{ 6, -1, 7, 5, 0, 4, 3},
{ 0, 0, 0, 0, 7, 5, -1} },
{ /* H/V: xt,yt on +1 */
{ 1, -1, 0, 2, 3, 7, 6},
{ 0, 0, 0, 0, 2, 8, 8} },
{ /* H/V: xt,yt on 0 */
{ 0, -1, 4, 1, 7, 2, 6},
{ 0, 0, -1, 0, 0, 0, 0} },
{ /* H/V: xt,yt on -1 */
{ 7, -1, 0, 6, 5, 1, 2},
{ 0, 0, 0, 0, 6, 8, 8} }
};
for (dt = 0; dt < 8; dt++) /* get direction of e->t */
if (xt == xe + dr[dt][0] && yt == ye + dr[dt][1])
break;
if (dt >= 8)
err ("pnt_eha: <xe,ye> not adjacent to <xt,yt>", xe, ye, xt, yt);
dt = (8 + dt - de) % 8; /* need it relative */
dt = (dt > 4) ? dt - 8 : dt;/* 0 ... 7 -> -3 ... 4 */
if ((de & 1 && (dt > 2 || dt < -2)) ||
(!(de & 1) && (dt > 1 || dt < -1)))
err ("pnt_eha: illegal case", xe, ye, xt, yt);
i = (de & 1) ? 2 - dt : 6 - dt;/* select case (see table) */
color (ishb | selb, ishb | selb);
for (j = 0; j < 7; j++) { /* scan through cases */
if (hasp[i].r[j] < 0) {
x = xe;
y = ye;}
else {
x = xe + dr[(hasp[i].r[j] + de) % 8][0];
y = ye + dr[(hasp[i].r[j] + de) % 8][1];
}
if ((x | y) & 1 || pin (x, y))
continue; /* not aligned or no space */
color (b | selb, b | selb);
plt (xe, ye, trc_buf[trc_cnt - 1].x, trc_buf[trc_cnt - 1].y);
color (vec, vec);
pxl (x, y); /* via hole is inserted */
if (hasp[i].h[j]) { /* need a intermediate point */
color (b ^ vec, b ^ vec);
pxl ((hasp[i].h[j] < 0) ? xe : xe + dr[(hasp[i].h[j] + de) % 8][0],
(hasp[i].h[j] < 0) ? ye : ye + dr[(hasp[i].h[j] + de) % 8][1]);
}
ckgp (x, y, b);
return (2);
}
err_msg ("No space for a hole");
beep ();
color (b | selb, b | selb);
return (1);
}
viaalg (b) /* via hole alignment */
int b;
/********************************************************************\
* *
* tries to insert a via-hole at <trc_buf[trc_cnt-1].x/y> to allow *
* continuation on side <b>. A intermediate vector might be added to *
* the trace buffer. trc_buf is assumed to have enough space. *
* *
* Returns a 0 if succesful, a 1 otherwise *
* *
\********************************************************************/
{
int i, j, x, y, dx, dy;
static int ct[9] = {5, 4, 3, 6, 8, 2, 7, 0, 1};
x = trc_buf[trc_cnt - 1].x;
y = trc_buf[trc_cnt - 1].y;
color (ishb | selb, ishb | selb);
if (!((x | y) & 1)) { /* already aligned */
if (pin (x, y))
return (1); /* no space */
else {
color (vec, vec);
pxl (x, y);
return (0); } /* done ! */
}
if (trc_cnt > 1) { /* any prefered direction ? */
dx = (trc_buf[trc_cnt - 2].x < x) - (trc_buf[trc_cnt - 2].x > x);
dy = (trc_buf[trc_cnt - 2].y < y) - (trc_buf[trc_cnt - 2].y > y);
if (!((x + dx) & 1) && !((y + dy) & 1) && !pin (x + dx, y + dy)) {
/* extend previous vector */
x = trc_buf[trc_cnt - 1].x += dx;
y = trc_buf[trc_cnt - 1].y += dy;
color (vec, vec);
pxl (x, y);
return (0);
}
if (!((x - dx) & 1) && !((y - dy) & 1) && !pin (x - dx, y - dy)) {
/* shorten previous vector */
trc_buf[trc_cnt - 1].x -= dx;
trc_buf[trc_cnt - 1].y -= dy;
color (vec, vec);
pxl (trc_buf[trc_cnt - 1].x, trc_buf[trc_cnt - 1].y);
color (0, vec ^ b);
pxl (x, y);
return (0);
}
}
for (i = 0; i < 8; ++i) /* brute force search */
if (!((x + dr[i][0]) & 1) && !((y + dr[i][1]) & 1) &&
!pin (x + dr[i][0], y + dr[i][1])) {
if (trc_cnt > 1) {
j = ct[(dx + 1) * 3 + dy + 1];
if (((i + 3) % 8 == j) || ((i + 4) % 8 == j) ||
((i + 5) % 8 == j)) { /* avoid dead maze */
color (0, vec ^ b);
pxl (trc_buf[trc_cnt - 1].x, trc_buf[trc_cnt - 1].y);
trc_buf[trc_cnt - 1].x -= dx;
trc_buf[trc_cnt - 1].y -= dy;
}
}
color (vec, vec);
pxl (trc_buf[trc_cnt].x = dr[i][0] + x,
trc_buf[trc_cnt].y = dr[i][1] + y);
trc_buf[trc_cnt++].ty = vec ^ b;
return (0);
}
return (1); /* there is just no space */
}
static int dex = 0; /* delete exit feature */
m_delete (x, y, ctx) /* manual delete */
int x, y, ctx;
{
plow_f = 0;
if (del (x, y, vec)) {
if (dex) { /* switch to an other net */
dex = 0;
if (V.cnet)
deseln (V.cnet);
return (net_sel (ctx));}
else
dex = 1;
}
return (START);
}
mr_delete (x, y, ctx) /* manual delete in r-context */
int x, y, ctx;
{
int dx, dy;
plow_f = 0;
dex = 0;
if (del (x, y, vec)) { /* del failed: backtrack */
if (trc_cnt > 1) {
x = trc_buf[trc_cnt - 1].x;
y = trc_buf[trc_cnt - 1].y;
dx = (trc_buf[trc_cnt - 2].x < x) - (trc_buf[trc_cnt - 2].x > x);
dy = (trc_buf[trc_cnt - 2].y < y) - (trc_buf[trc_cnt - 2].y > y);
color (0, selb | (trc_buf[trc_cnt - 1].ty & vec));
plt (x, y, trc_buf[trc_cnt - 2].x + dx, trc_buf[trc_cnt - 2].y + dy);
if (trc_buf[trc_cnt - 1].ty & ishb) {
color (0, selb | vec & trc_buf[trc_cnt - 1].ty);
ck_rdnb (x = trc_buf[trc_cnt - 2].x,
y = trc_buf[trc_cnt - 2].y, vec & trc_buf[trc_cnt - 1].ty);
if(pcb[y][x] & ishb) {
color (0, ishb | selb);
dpin (x, y);
color (selb, selb);
/* guard zones may stay unselected: cosmetic defect */
for (dy = -1; dy <= 1; dy++)/* un-unselect */
for (dx = -1; dx <= 1; dx++)
if (pcb[y + dy][x + dx] & vec)
pxl (x + dx, y + dy);
}else{
printf("This was the bug - please report\n");
beep();
}
if (trc_cnt > 2 &&
abs(trc_buf[trc_cnt - 3].x - trc_buf[trc_cnt - 2].x) <2 &&
abs(trc_buf[trc_cnt - 3].y - trc_buf[trc_cnt - 2].y) <2) {
/* remove alignment stub */
color (0, selb | trc_buf[trc_cnt - 2].ty);
ck_rdnb (trc_buf[trc_cnt - 3].x, trc_buf[trc_cnt - 3].y,
trc_buf[trc_cnt - 2].ty);
trc_cnt--;
}
}
trc_cnt--;
}
if (trc_cnt <= 1) { /* unselect start */
ckgp (trc_buf[0].x, trc_buf[0].y, s1b);
ckgp (x = trc_buf[0].x, y = trc_buf[0].y, s2b);
color (selb, selb);
for (dy = -1; dy <= 1; dy++)/* un-unselect */
for (dx = -1; dx <= 1; dx++)
if (pcb[y + dy][x + dx] & (sof_seg | ahb))
pxl (x + dx, y + dy);
trc_cnt = 0;
return (START);
}
}
return (ctx);
}
static int wxl, wyl, wxh, wyh; /* window extensions */
/*ARGSUSED*/
cr_window (ctx) /* create a window */
int ctx;
{
wxl = wx + 192 / cz; /* default size */
wyl = wy + 177 / cz;
wxh = wx + 320 / cz;
wyh = wy + 305 / cz;
di_window ();
}
di_window () /* display window */
{
color (resb, resb);
plts (wxl, wyl, wxh, wyl);
plts (wxh, wyl, wxh, wyh);
plts (wxh, wyh, wxl, wyh);
plts (wxl, wyh, wxl, wyl);
}
rm_window () /* remove window */
{
color (0, resb);
plts (wxl, wyl, wxh, wyl);
plts (wxh, wyl, wxh, wyh);
plts (wxh, wyh, wxl, wyh);
plts (wxl, wyh, wxl, wyl);
}
/*ARGSUSED*/
w_zoomi (x, y, ctx) /* zoom in (and maintain window) */
int x, y, ctx;
{
if (cz < 16) {
rm_window ();
zoom (cz + 1);
di_window ();
}
return (ctx); /* don't change context */
}
/*ARGSUSED*/
w_zoomo (x, y, ctx) /* zoom out (and maintain window) */
int x, y, ctx;
{
if (cz > 1) {
rm_window ();
zoom (cz - 1);
di_window ();
}
return (ctx); /* don't change context */
}
w_wmv (x, y, ctx) /* move window */
int x, y, ctx;
{
rm_window ();
window (x - 256 / cz, y - 256 / cz);
di_window ();
return (ctx); /* don't change context */
}
wnd_urc (x, y, ctx) /* window: upper right corner */
int x, y, ctx;
{
rm_window ();
if (x >= wxl)
wxh = x;
else {
wxh = wxl;
wxl = x;
}
if (y >= wyl)
wyh = y;
else {
wyh = wyl;
wyl = y;
}
di_window ();
return (ctx);
}
wnd_llc (x, y, ctx) /* window: lower left corner */
int x, y, ctx;
{
rm_window ();
wxh = x + (wxh - wxl);
wxl = x;
wyh = y + (wyh - wyl);
wyl = y;
di_window ();
return (ctx);
}
static int ad_ty; /* area delete type */
ini_adel (ctx) /* initialize area delete */
int ctx;
{
ad_ty = vec;
cr_window (ctx);
return (ctx);
}
/*ARGSUSED*/
sel_adel (x, y, ctx) /* select area delete options */
int x, y, ctx;
{
static char *txt[] = {
"Everything",
"Side 1 only",
"Side 2 only",
"Nothing"
};
switch (menu (txt, 4)) {
case 0:
ad_ty = vec;
break;
case 1:
ad_ty = s1b;
break;
case 2:
ad_ty = s2b;
break;
default:
err_msg ("Area-delete aborted");
rm_window ();
return (START);
}
return (ctx);
}
/*ARGSUSED*/
exc_adel (x, y, ctx) /* execute area delete */
int x, y, ctx;
{
int ad_hck (), ad_vck ();
struct nlhd *t, *get_net ();
register int xx, yy;
rm_window ();
for (yy = wyl + 1; yy < wyh; yy++)/* delete loop */
for (xx = wxl + 1; xx < wxh; xx++)
if (pcb[yy][xx] & ad_ty) {
t = get_net (xx, yy, vec);
if (t < &NH[0] || t > &NH[V.nnh])
continue; /* skip power and ground nets */
wthf = ad_hck;
wtvf = ad_vck;
wtrace (xx, yy, vec);
if (t && t -> f && !cchk (t)) {
t -> f = 0;
nettgo++;
}
}
for (xx = wxl; xx <= wxh; xx++) {/* clean edges */
if (pcb[wyl][xx] & (ahb | vec))
ad_gchk (xx, wyl);
if (pcb[wyh][xx] & (ahb | vec))
ad_gchk (xx, wyh);
}
for (yy = wyl + 1; yy < wyh; yy++) {
if (pcb[yy][wxl] & (ahb | vec))
ad_gchk (wxl, yy);
if (pcb[yy][wxh] & (ahb | vec))
ad_gchk (wxh, yy);
}
nets_msg (nettgo); /* update number of nets message */
return (START);
}
ad_hck (x, y) /* area-delete hole check */
int x, y;
{
if (pcb[y][x] & ishb && x > wxl && y > wyl && x < wxh && y < wyh) {
color (0, ishb);
dpin (x, y);
}
return (0);
}
ad_vck (x1, y1, x2, y2) /* area-delete vector check */
int x1, y1, x2, y2;
{
int i;
if (y1 > y2) { /* order y for y-band clipping */
i = x1;
x1 = x2;
x2 = i;
i = y1;
y1 = y2;
y2 = i;
}
i = ((x1 < x2) - (x2 < x1));
if (y1 <= wyl) {
if (y2 <= wyl)
return;
x1 += (wyl + 1 - y1) * i;
y1 = wyl + 1;
};
if (y2 >= wyh) {
if (y1 >= wyh)
return;
x2 -= (y2 - (wyh - 1)) * i;
y2 = wyh - 1;
}
if (x1 > x2) { /* order x for x-band clipping */
i = x1;
x1 = x2;
x2 = i;
i = y1;
y1 = y2;
y2 = i;
}
i = (y1 < y2) - (y2 < y1);
if (x1 <= wxl) {
if (x2 <= wxl)
return;
y1 += (wxl + 1 - x1) * i;
x1 = wxl + 1;
}
if (x2 >= wxh) {
if (x1 >= wxh)
return;
y2 -= (x2 - (wxh - 1)) * i;
x2 = wxh - 1;
}
if (abs (x1 - x2) < 2 && abs (y1 - y2) < 2 &&
pcb[x1][y1] & chb && pcb[x2][y2] & chb)
return; /* do not remove parts of a hole */
color (0, wtsb);
plt (x1, y1, x2, y2);
}
ad_gchk (x, y) /* area delete: garbage check */
int x, y;
{
struct nlhd *get_net ();
int delh (), delv ();
if (!get_net (x, y, vec)) {
wthf = delh;
wtvf = delv;
wtrace (x, y, vec);
}
}
ini_art (ctx) /* initialize area route */
int ctx;
{
cr_window (ctx);
return (ctx);
}
/*ARGSUSED*/
sel_art (x, y, ctx) /* select area route options */
int x, y, ctx;
{
err_msg ("Area-route has no options yet");
return (ctx);
}
/*ARGSUSED*/
exc_art (x, y, ctx) /* execute area route */
int x, y, ctx;
{
register int xx, yy, max, cnt, i;
struct nlhd **nl, *t, *get_net ();
char buf[40];
max = 200; /* assume 200 for a start */
cnt = 0;
nl = (struct nlhd **) malloc (sizeof (struct nlhd *) * max);
rm_window ();
Ferr_msg ("Busy: routing");
for (yy = wyl + 1; yy < wyh; yy++)
for (xx = wxl + 1; xx < wxh; xx++)
if ((pcb[yy][xx] & chb) && (t = get_net (xx, yy, vec)) && !(t -> f)) {
for (i = 0; i < cnt; i++)
if (nl[i] == t)
break; /* got this net already */
if (i >= cnt) {
if (cnt >= max) {
max += max / 2;
nl = (struct nlhd **) realloc
(nl, sizeof (struct nlhd *) * max);
}
nl[cnt++] = t;
}
}
for (i = 0; i < cnt; i++) {
sprintf (buf, "Routing net %d of %d", i, cnt);
nets_msg (nettgo);
Ferr_msg (buf);
N_route (nl[i], 2, wxl, wyl, wxh, wyh);
nettgo -= nl[i] -> f;
}
free (nl); /* clean up */
nets_msg (nettgo);
err_msg ("Done");
beep ();
return (START);
}
!E!O!F!
#
# type sh /usru0/agn/pcb/distr/../usenet/part7 to unpack this archive.
#
echo extracting pplace.c...
cat >pplace.c <<'!E!O!F!'
/***************************************************************\
* *
* PCB program *
* *
* Placement routines *
* *
* (c) 1985 A. Nowatzyk *
* *
\***************************************************************/
#include <stdio.h>
#include "pparm.h"
#include "pcdst.h"
int rot_m[4][4] = { { 1, 0, 0, 1},
{ 0, -1, 1, 0},
{-1, 0, 0, -1},
{ 0, 1, -1, 0} };
fnd_cmp (x, y) /* find component */
int x, y;
{
register int i, xl, xh, yl, yh;
for (i = 0; i < V.ncp; i++) {
switch (CP[i].r) {
case 0:
xl = CP[i].x;
yl = CP[i].y;
xh = xl + CP[i].ty -> x;
yh = yl + CP[i].ty -> y;
break;
case 1:
xh = CP[i].x;
yl = CP[i].y;
xl = xh - CP[i].ty -> y;
yh = yl + CP[i].ty -> x;
break;
case 2:
xh = CP[i].x;
yh = CP[i].y;
xl = xh - CP[i].ty -> x;
yl = yh - CP[i].ty -> y;
break;
case 3:
xl = CP[i].x;
yh = CP[i].y;
xh = xl + CP[i].ty -> y;
yl = yh - CP[i].ty -> x;
break;
}
xl--;
yl--;
xh++;
yh++;
if (x <= xh && x >= xl && y <= yh && y >= yl)
return (i);
}
return (-1); /* no component found */
}
draw_cp (x, y, r, cp) /* draw component shape */
int x, y, r, cp;
{
register int i, xl, yl, xh, yh;
register struct pin *pp;
int j;
switch (r) {
case 0:
xl = x;
yl = y;
xh = xl + CP[cp].ty -> x;
yh = yl + CP[cp].ty -> y;
break;
case 1:
xh = x;
yl = y;
xl = xh - CP[cp].ty -> y;
yh = yl + CP[cp].ty -> x;
break;
case 2:
xh = x;
yh = y;
xl = xh - CP[cp].ty -> x;
yl = yh - CP[cp].ty -> y;
break;
case 3:
xl = x;
yh = y;
xh = xl + CP[cp].ty -> y;
yl = yh - CP[cp].ty -> x;
break;
}
xl -= 2;
yl -= 2;
xh += 2;
yh += 2;
color (resb, resb);
plts (xl, yl, xh, yl);
plts (xh, yl, xh, yh);
plts (xh, yh, xl, yh);
plts (xl, yh, xl, yl);
for (j = 0, i = CP[cp].ty -> np, pp = CP[cp].ty -> p; i; i--, pp++) {
xl = x + (pp -> x) * rot_m[r][0] + (pp -> y) * rot_m[r][1];
yl = y + (pp -> x) * rot_m[r][2] + (pp -> y) * rot_m[r][3];
if (!j) {
j = 1;
dot (xl, yl);
}
else
point (xl, yl);
}
}
del_cp (x, y, r, cp) /* delete component shape */
int x, y, r, cp;
{
register int xl, yl, xh, yh;
switch (r) {
case 0:
xl = x;
yl = y;
xh = xl + CP[cp].ty -> x;
yh = yl + CP[cp].ty -> y;
break;
case 1:
xh = x;
yl = y;
xl = xh - CP[cp].ty -> y;
yh = yl + CP[cp].ty -> x;
break;
case 2:
xh = x;
yh = y;
xl = xh - CP[cp].ty -> x;
yl = yh - CP[cp].ty -> y;
break;
case 3:
xl = x;
yh = y;
xh = xl + CP[cp].ty -> y;
yl = yh - CP[cp].ty -> x;
break;
}
color (0, resb);
c_rect (xl - 2, yl - 2, xh + 2, yh + 2);
}
static int active = 0; /* !=0 if some coponent is sel */
static int cp_id, cp_x, cp_y, cp_r;
cmv_sel (x, y, ctx) /* select a component */
int x, y, ctx;
{
char buf[nmmax + nmmax + 3];
if (active) /* remove previous selection */
del_cp (cp_x, cp_y, cp_r, cp_id);
cp_id = fnd_cmp (x, y);
if (cp_id < 0) {
active = 0;
err_msg ("No component found");}
else {
active = 1;
cp_x = CP[cp_id].x;
cp_y = CP[cp_id].y;
cp_r = CP[cp_id].r;
draw_cp (cp_x, cp_y, cp_r, cp_id);
sprintf (buf, "%s (%s)", CP[cp_id].name, CP[cp_id].ty -> name);
err_msg (buf);
}
return (ctx);
}
/*ARGSUSED*/
exc_cmv (x, y, ctx) /* execute CP move */
int x, y, ctx;
{
register int i, x1, y1, or;
register struct pin *pp;
if (!active)
return START; /* nothing to do */
del_cp (cp_x, cp_y, cp_r, cp_id);
active = 0;
if (CP[cp_id].r == cp_r && CP[cp_id].x == cp_x && CP[cp_id].y == cp_y) {
err_msg ("Not moved");
return START; /* move to same location */
}
or = CP[cp_id].r; /* get old rotation */
for (i = CP[cp_id].ty -> np, pp = CP[cp_id].ty -> p; i; i--, pp++) {
/* check alignment of new location */
x1 = cp_x + (pp -> x) * rot_m[cp_r][0] + (pp -> y) * rot_m[cp_r][1];
y1 = cp_y + (pp -> x) * rot_m[cp_r][2] + (pp -> y) * rot_m[cp_r][3];
if (cp_alchk (x1) || cp_alchk (y1)) {
err_msg ("Pins not on drill grid");
return START;
}
/* check range */
if (x1 < 3 || y1 < 3 || x1 > V.BSX - 3 || y1 > V.BSY - 3) {
err_msg ("Can't move off board");
return START;
}
/* check for wires */
x1 = CP[cp_id].x + pp -> x * rot_m[or][0] + pp -> y * rot_m[or][1];
y1 = CP[cp_id].y + pp -> x * rot_m[or][2] + pp -> y * rot_m[or][3];
if (pcb[y1][x1] & (s1b | s2b)) {
err_msg ("Disconnect wires first");
return START;
}
}
x1 = CP[cp_id].x; /* save old place */
y1 = CP[cp_id].y;
i = CP[cp_id].r;
if (!C_unplace (&CP[cp_id]))
err ("exc_cmv: this should not happen", cp_id, 0, 0, 0);
if (!C_place (&CP[cp_id], cp_x, cp_y, cp_r)) {
err_msg ("No space at new place");
if (!C_place (&CP[cp_id], x1, y1, i))
err ("exc_cmv: re-insert to old place failed", x1, y1, i, cp_id);
}
CH_update ();
return START;
}
/*ARGSUSED*/
cmv_rr (x, y, ctx) /* rotate right */
int x, y, ctx;
{
if (active) {
del_cp (cp_x, cp_y, cp_r, cp_id);
cp_r = 3 & (cp_r + 3);
draw_cp (cp_x, cp_y, cp_r, cp_id);}
else
err_msg ("Select a CP first");
return (ctx);
}
/*ARGSUSED*/
cmv_rl (x, y, ctx) /* rotate left */
int x, y, ctx;
{
if (active) {
del_cp (cp_x, cp_y, cp_r, cp_id);
cp_r = 3 & (cp_r + 1);
draw_cp (cp_x, cp_y, cp_r, cp_id);}
else
err_msg ("Select a CP first");
return (ctx);
}
cmv_plc (x, y, ctx) /* place component */
int x, y, ctx;
{
char buf[40];
if (active) {
del_cp (cp_x, cp_y, cp_r, cp_id);
cp_x = cp_align (x);
cp_y = cp_align (y);
draw_cp (cp_x, cp_y, cp_r, cp_id);
sprintf (buf, "X=%6.3f\" Y=%6.3f\"",
((float) x / rsun) * 0.1, ((float) y / rsun) * 0.1);
err_msg (buf);}
else
err_msg ("Select a CP first");
return (ctx);
}
C_place (cmp, x, y, r)
struct comp *cmp;
int x, y, r;
/****************************************************************\
* *
* C_place tries to place the component <cmp> on location <x,y> *
* with rotation <r>. If it succeedes, the component entry will *
* be updated and a '1' is returned. A returned '0' indicates *
* that the desired placement was not possible due to design *
* rule constraints. *
* *
\****************************************************************/
{
#define xtr(a, b) (x + a * rot_m[r][0] + b * rot_m[r][1])
#define ytr(a, b) (y + a * rot_m[r][2] + b * rot_m[r][3])
register struct pin *pp;
register int i, j, k, x1, y1;
int x2, y2;
if (!(cmp -> unplaced))
err ("C_place: already placed", cmp, x, y, r);
if (r < 0 || r > 3 || x < 3 || y < 3 || x > V.BSX - 3 || y > V.BSX - 3)
return 0; /* illegal paramenter */
for (i = cmp -> ty -> np, pp = cmp -> ty -> p; i; pp++, i--) {
x1 = xtr (pp -> x, pp -> y);
y1 = ytr (pp -> x, pp -> y);
if( x1 < 3 || x1 > V.BSX - 3 || y1 < 3 || y1 > V.BSY - 3)
return 0; /* pin is off board */
if (cp_alchk (x1) || cp_alchk (y1))
return 0; /* illegal pin alignment */
if (!ck_pin (x1, y1))
return 0; /* overlapp problem */
}
if (cmp -> ty -> cif) /* external CIF type: block check*/
for (i = cmp -> ty -> cif - 1; CIF[i].blk; i++) {
for (y1 = CIF[i].yl - 1; y1 <= CIF[i].yh + 1; y1++)
for (x1 = CIF[i].xl - 1; x1 <= CIF[i].xh + 1; x1++)
if (pcb[ytr (x1, y1)][xtr (x1, y1)] & (fb | ahb | vec))
return 0; /* Block problem */
if (!CIF[i].flg)
break;
}
/**** everthing looks fine: proceed to insert component ****/
color (chb, chb); /* insert pins */
for (i = cmp -> ty -> np, pp = cmp -> ty -> p; i; pp++, i--)
if (pin (xtr (pp -> x, pp -> y), ytr (pp -> x, pp -> y))) {
printf ("Type definition error for '%s': pins too close\n",
cmp -> ty -> name);
color (0, chb);
for (pp = cmp -> ty -> p; i < cmp -> ty -> np; pp++, i++)
dpin (xtr (pp -> x, pp -> y), ytr (pp -> x, pp -> y));
return 0;
}
if (cmp -> ty -> cif) { /* insert road blocks */
color (fb, fb);
for (i = cmp -> ty -> cif - 1; CIF[i].blk; i++) {
x1 = xtr (CIF[i].xl, CIF[i].yl);
y1 = ytr (CIF[i].xl, CIF[i].yl);
x2 = xtr (CIF[i].xh, CIF[i].yh);
y2 = ytr (CIF[i].xh, CIF[i].yh);
if (x1 > x2) {j = x1; x1 = x2; x2 = j;}
if (y1 > y2) {j = y1; y1 = y2; y2 = j;}
c_rect (x1, y1, x2, y2);
for (; y1 <= y2; y1++)
for (k = x1; k <= x2; k++)
pcb[y1][k] |= fb;
if (!CIF[i].flg)
break;
}
}
k = ((int) cmp - (int) CP) / sizeof (struct comp); /* get own index */
pp = cmp -> ty -> p;
for (i = 0, j = 0; i < V.nch; i++)
if (CH[i].cpi == k) {
j++; /* count holes */
CH[i].x = xtr (pp[CH[i].pn].x, pp[CH[i].pn].y);
CH[i].y = ytr (pp[CH[i].pn].x, pp[CH[i].pn].y);
}
if (j != cmp -> ty -> np)
err ("C_place: inconsistent pin numbers", j, cmp -> ty -> np, 0, 0);
cmp -> x = x; /* update component entry */
cmp -> y = y;
cmp -> r = r;
cmp -> unplaced = 0;
placed = 0; /* insure update */
return 1; /* finally done! */
#undef xtr
#undef ytr
}
C_unplace (cmp)
struct comp *cmp;
/***************************************************************\
* *
* C_unplace tries to remove the component <cmp> from its *
* current location. It returns a '1' if it succeeds. *
* A returned '0' indicates that the opaeration failed because *
* the component had wires connected to it. *
* *
\***************************************************************/
{
#define xtr(a, b) (x + a * rot_m[r][0] + b * rot_m[r][1])
#define ytr(a, b) (y + a * rot_m[r][2] + b * rot_m[r][3])
register struct pin *pp;
register int i, x1, x = cmp -> x, y = cmp -> y, r = cmp -> r;
int j, y1, x2, y2;
struct hole *fndh (), *hp;
static struct hole **chl = 0;
static int chlmax = 100;
if (cmp -> unplaced)
return 1; /* is already unplaced */
for (i = cmp -> ty -> np, pp = cmp -> ty -> p; i; pp++, i--) {
if (pcb[ytr (pp -> x, pp -> y)][xtr (pp -> x, pp -> y)] & vec)
return 0; /* no wires allowed */
/**** everthing looks fine: proceed to remove component ****/
if (!chl) { /* allocate temp table */
if (chlmax < cmp -> ty -> np)
chlmax = 10 + cmp -> ty -> np;
chl = (struct hole **) malloc (sizeof (struct hole *) * chlmax);}
else if (chlmax < cmp -> ty -> np) {
free (chl);
chlmax = 10 + cmp -> ty -> np;
chl = (struct hole **) malloc (sizeof (struct hole *) * chlmax);}
}
color (0, selb | chb); /* remove pins */
for (i = cmp -> ty -> np, pp = cmp -> ty -> p; i; pp++) {
x1 = xtr (pp -> x, pp -> y);
y1 = ytr (pp -> x, pp -> y);
hp = fndh (x1, y1);
if (!hp)
err ("C_unplace: missing hole", x1, y1, i, 0);
chl[--i] = hp; /* save hole pointer */
dpin (x1, y1);
}
for (i = cmp -> ty -> np; i;) {
chl[--i] -> x = 0; /* invalidate cooradinates */
chl[i] -> y = 0;
}
if (cmp -> ty -> cif) { /* remove road blocks */
color (0, fb);
for (i = cmp -> ty -> cif - 1; CIF[i].blk; i++) {
x1 = xtr (CIF[i].xl, CIF[i].yl);
y1 = ytr (CIF[i].xl, CIF[i].yl);
x2 = xtr (CIF[i].xh, CIF[i].yh);
y2 = ytr (CIF[i].xh, CIF[i].yh);
if (x1 > x2) {j = x1; x1 = x2; x2 = j;}
if (y1 > y2) {j = y1; y1 = y2; y2 = j;}
c_rect (x1, y1, x2, y2);
for (; y1 <= y2; y1++)
for (j = x1; j <= x2; j++)
pcb[y1][j] &= ~fb;
if (!CIF[i].flg)
break;
}
}
placed = 0; /* no longer placed */
cmp -> unplaced = 1;
flush_stat (); /* statistics are invalid */
return 1; /* finally done! */
#undef xtr
#undef ytr
}
CH_update ()
/****************************************************\
* *
* Sort the hole table and update the pointer to it *
* (this is prerequisite for 'fndh' to work) *
* *
\****************************************************/
{
register int i;
int key ();
qsort (CH, V.nch, sizeof (CH[0]), key); /* sort holes */
for (i = 0; i < V.nch; i++) /* adj pointers */
if (CH[i].n)
CH[i].n -> c = &CH[i];
}
key (h1, h2) /* is used to sort the holes */
struct hole *h1, *h2;
{
if (h1 -> x > h2 -> x)
return 1;
if (h1 -> x < h2 -> x)
return -1;
if (h1 -> y > h2 -> y)
return 1;
if (h1 -> y < h2 -> y)
return -1;
return 0;
}
ck_placed ()
/****************************************************************\
* *
* Check placement: *
* returns a '1' if all components are placed (and updates the *
* net location info). A '0' is returned otherwise. *
* *
\****************************************************************/
{
register int i, j, xl, yl, xh;
register struct nlst *p;
int yh;
for (i = 0, xl = V.ncp; i < xl; i++)
if (CP[i].unplaced)
return 0;
for (i = 0; i < V.nnh; i++) /* update net locations */
if (NH[i].l) {
xl = xmax;
yl = ymax;
xh = 0;
yh = 0;
for (p = NH[i].lp; p; p = p -> n) {
j = p -> c -> x;
if (j > xh) xh = j;
if (j < xl) xl = j;
j = p -> c -> y;
if (j > yh) yh = j;
if (j < yl) yl = j;
}
if (!xl)
err ("ck_placed: invlid hole coordinate", i, xl, yl, xh);
NH[i].x1 = xl;
NH[i].x2 = xh;
NH[i].y1 = yl;
NH[i].y2 = yh;
}
return 1;
}
CPp_ini (ctx) /* CP place initialization */
int ctx;
{
register int i;
char buf[nmmax + nmmax + 3];
if (ck_placed ()) {
active = 0;
return START; /* nothing to place */
}
for (i = 0; i < V.ncp; i++) /* find something to place */
if (CP[i].unplaced)
break;
cp_id = i;
cp_x = wx + 256 / cz;
cp_y = wy + 241 / cz;
cp_r = 0;
active = 1;
draw_cp (cp_x, cp_y, 0, i);
sprintf (buf, "%s (%s)", CP[cp_id].name, CP[cp_id].ty -> name);
err_msg (buf);
return ctx;
}
/*ARGSUSED*/
CPp_exc (x, y, ctx) /* try some other component */
int x, y, ctx;
{
if (C_place (&CP[cp_id], cp_x, cp_y, cp_r)) {
del_cp (cp_x, cp_y, cp_r, cp_id);
CH_update ();
return CPp_ini (ctx);
}
err_msg ("Invalid place");
return ctx;
}
CPp_del (x, y, ctx) /* delete a component */
int x, y, ctx;
{
register int i, j, k;
char buf[nmmax + nmmax + 3];
i = fnd_cmp (x, y);
if (i < 0) { /* try to select an other cmp */
k = V.ncp;
for (i = 0, j = cp_id + 1; i < k; i++)
if (CP[(i + j) % k].unplaced)
break;
i = (i + j) % k;
if (i == cp_id)
err_msg ("only one CMP left");
else {
del_cp (cp_x, cp_y, cp_r, cp_id);
cp_id = i;
cp_x = x;
cp_y = y;
cp_r = 0;
draw_cp (x, y, 0, i);
sprintf (buf, "%s (%s)", CP[cp_id].name, CP[cp_id].ty -> name);
err_msg (buf);
}}
else if (!C_unplace (&CP[i])) /* delete component */
err_msg ("Remove wires first");
CH_update ();
return ctx;
}
CPp_plc (x, y, ctx) /* tennativly place component */
int x, y, ctx;
{
char buf[40];
del_cp (cp_x, cp_y, cp_r, cp_id);
cp_x = cp_align (x);
cp_y = cp_align (y);
draw_cp (cp_x, cp_y, cp_r, cp_id);
sprintf (buf, "X=%6.3f\" Y=%6.3f\"",
((float) x / rsun) * 0.1, ((float) y / rsun) * 0.1);
err_msg (buf);
return ctx;
}
!E!O!F!
#
# type sh /usru0/agn/pcb/distr/../usenet/part7 to unpack this archive.
#
echo extracting psub.c...
cat >psub.c <<'!E!O!F!'
/***************************************************************\
* *
* PCB program *
* *
* Utility routines *
* *
* (c) 1985 A. Nowatzyk *
* *
\***************************************************************/
#include "pparm.h"
#include "pcdst.h"
#include <stdio.h>
err (s, e1, e2, e3, e4) /* error exit */
char s[];
int e1, e2, e3, e4;
{
printf ("Error: %s -- exited\n", s);
strncpy (V.errtxt, s, 80);
V.err_info[0] = e1;
V.err_info[1] = e2;
V.err_info[2] = e3;
V.err_info[3] = e4;
if (s[0] != '-') {
save (1); /* save state for recovery */
printf ("Data structure saved on 'pcb.ERR'\n");
};
finish ();
}
beep() /* beep on the terminal */
{
if (no_beep)
return;
putc (7, stdout);
fflush (stdout);
}
pin(x, y) /* enter pin to pcb and AED */
int x, y;
{
int i, j, k;
static int hx[8] = {3, 3, 0,-3,-3,-3, 0, 3};
static int hy[8] = {0, 3, 3, 3, 0,-3,-3,-3};
static int bx[12] = {5, 5, 5, 1, 0,-1,-5,-5,-5, 1, 0,-1};
static int by[12] = {1, 0,-1, 5, 5, 5, 1, 0,-1,-5,-5,-5};
static int xt[12] = {3, 3, 3, 1, 0,-1,-3,-3,-3, 1, 0,-1};
static int yt[12] = {1, 0,-1, 3, 3, 3, 1, 0,-1,-3,-3,-3};
for (i = x - 2; i <= x + 2; ++i)
for (j = y - 2; j <= y + 2; ++j) {
k = pcb[j][i];
if ((k & (fb | chb | ishb)) || (!(k & selb) && k & vec) ||
!(vec & ~k))
return 1; /* overlap error */
}
for (i = 0; i < 8; ++i) /* special hole distance */
if (pcb[y+hy[i]][x+hx[i]] & (ahb | fb))
return (1); /* too close to hole */
for (i = 0; i < 12; ++i) /* check for small passages */
if ((pcb[y+by[i]][x+bx[i]] & ahb) &&
((pcb[y+yt[i]][x+xt[i]] & vec) && !(pcb[y+yt[i]][x+xt[i]] & selb)))
return (1);
for (i = x - 1; i <= x + 1; ++i)
for (j = y - 1; j <= y + 1; ++j)
pcb[j][i] = ccc | (ccm & pcb[j][i]);
if (!batch)
dot (x, y); /* update AED */
for (i = 0; i < 12; ++i) /* block small passages */
if (pcb[y+by[i]][x+bx[i]] & ahb)
pxl (x + xt[i], y + yt[i]);
return (0);
}
dot (x, y) /* plot a 3 by 3 rectangle on AED*/
int x, y;
{
int x1, y1, x2, y2;
x1 = (x - 1 > fx) ? x - 1 : fx;
x2 = (x < fx + 510) ? x + 1 : fx + 511;
y1 = (y - 1 > fy) ? y - 1 : fy;
y2 = (y < fy + 510) ? y + 1 : fy + 511;
if ((x1 <= x2) && (y1 <= y2))
rect (x1, y1, x2, y2);
}
dpin (x, y) /* dot on pcb and AED (delete pin) */
int x, y;
{
int i, j, s, t;
static int xt[12] = {3, 3, 3, 1, 0, -1, -3, -3, -3, 1, 0, -1};
static int yt[12] = {1, 0, -1, 3, 3, 3, 1, 0, -1, -3, -3, -3};
s = (~ccm & ahb) ? pcb[y][x] & selb : 0; /* select prot ? */
for (i = 0; i < 12; ++i)/* remove guard zones */
if (pcb[y + yt[i]][x + xt[i]] & ahb) {
t = ccc | (ccm & pcb[y + yt[i]][x + xt[i]]);
pcb[y + yt[i]][x + xt[i]] = (s && t & vec) ? t | s : t;
if (!batch)
point (x + xt[i], y + yt[i]);
}
for (i = x - 1; i <= x + 1; ++i)
for (j = y - 1; j <= y + 1; ++j) {
t = ccc | (ccm & pcb[j][i]);
pcb[j][i] = (t & vec) ? t | s : t;
}
if (!batch)
dot (x, y); /* update AED */
return 0; /* this 0 is used, even if lint does not think so :-) */
}
ck_pin (x, y) /* check possibe via hole position */
int x, y;
/*******************************************************************\
* *
* A 1 is returned if it is possible to insert a via-hole at <x,y> *
* *
\*******************************************************************/
{
int i, j, k;
static int hx[8] = {3, 3, 0,-3,-3,-3, 0, 3};
static int hy[8] = {0, 3, 3, 3, 0,-3,-3,-3};
static int bx[12] = {5, 5, 5, 1, 0,-1,-5,-5,-5, 1, 0,-1};
static int by[12] = {1, 0,-1, 5, 5, 5, 1, 0,-1,-5,-5,-5};
static int xt[12] = {3, 3, 3, 1, 0,-1,-3,-3,-3, 1, 0,-1};
static int yt[12] = {1, 0,-1, 3, 3, 3, 1, 0,-1,-3,-3,-3};
for (i = x - 2; i <= x + 2; ++i)
for (j = y - 2; j <= y + 2; ++j) {
k = pcb[j][i];
if ((k & (fb | chb | ishb)) || ((k & vec) && !(k & selb)) ||
!(vec & ~k))
return 0; /* overlap error */
}
for (i = 0; i < 8; ++i) /* special hole distance */
if (pcb[y+hy[i]][x+hx[i]] & (ahb | fb))
return (0); /* too close to hole */
for (i = 0; i < 12; ++i) /* check for small passages */
if ((pcb[y+by[i]][x+bx[i]] & ahb) &&
((pcb[y+yt[i]][x+xt[i]] & vec) && !(pcb[y+yt[i]][x+xt[i]] & selb)))
return (0);
return (1); /* good place for a hole */
}
plt(x1, y1, x2, y2) /* Plot a line */
int x1, y1, x2, y2;
{
int dx, dy, n;
if ((x1 != x2) && (y1 != y2) && (abs (x1 - x2) != abs (y1 - y2)))
err ("invalid slope", x1, y1, x2, y2);
if (!batch)
plts (x1, y1, x2, y2);
dx = (x1 < x2) - (x2 < x1);
dy = (y1 < y2) - (y2 < y1);
n = abs (x1 - x2);
if (!n)
n = abs (y2 - y1);
while (n >= 0) {
pcb[y1][x1] = ccc | (ccm & pcb[y1][x1]);
x1 += dx;
y1 += dy;
n--;
};
};
plts (x1, y1, x2, y2) /* plot a line on the AED */
int x1, y1, x2, y2;
{
int t;
if (batch)
return;
if (y1 > y2) { /* order y for y-band clipping */
t = x1;
x1 = x2;
x2 = t;
t = y1;
y1 = y2;
y2 = t;
};
t = ((x1 < x2) - (x2 < x1));
if (y1 < fy) {
if (y2 < fy)
return;
x1 += (fy - y1) * t;
y1 = fy;
};
if (y2 > (fy + 511)) {
if (y1 > (fy + 511))
return;
x2 -= (y2 - (fy + 511)) * t;
y2 = fy + 511;
};
if (x1 > x2) { /* order x for x-band clipping */
t = x1;
x1 = x2;
x2 = t;
t = y1;
y1 = y2;
y2 = t;
};
t = (y1 < y2) - (y2 < y1);
if (x1 < fx) {
if (x2 < fx)
return;
y1 += (fx - x1) * t;
x1 = fx;
};
if (x2 > (fx + 511)) {
if (x1 > (fx + 511))
return;
y2 -= (x2 - (fx + 511)) * t;
x2 = fx + 511;
};
if (x1 <= x2)
plts1 (x1, y1, x2, y2);
else
plts1 (x2, y2, x1, y1);
}
plts1 (x1, y1, x2, y2) /* plot line on aed */
int x1, y1, x2, y2;
{
int t;
if ((x1 & 0xfffffe00) != (x2 & 0xfffffe00)) {
t = (y2 > y1) - (y2 < y1);
x1 &= 511;
plts1 (0, y1 + (512 - x1) * t, x2 & 511, y2);
x2 = 511;
y2 = y1 + (511 - x1) * t;
};
if (y2 < y1) { /* this could be optimized with more thinking */
t = y1;
y1 = y2;
y2 = t;
t = x1;
x1 = x2;
x2 = t;
};
if ((y1 & 0xfffffe00) != (y2 & 0xfffffe00)) {
t = (x2 > x1) - (x2 < x1);
y1 &= 511;
plts1 (x1 + (512 - y1) * t, 0, x2, y2 & 511);
y2 = 511;
x2 = x1 + (511 - y1) * t;
};
move (x1, y1);
draw (x2, y2);
}
menu (s, n) /* menu function select */
char *s[];
int n;
/*********************************************************************\
* *
* The <n> strings will be displayed on the screen. If the cursor *
* is clicked (any key) on a string, the coresponding number <0..n-1> *
* is returned. Otherwise, a -1 will be returned. *
* *
\*********************************************************************/
{
int i, j, x, y;
if (cz > 2) /* No menu on large zooms */
zoom (2);
msg_off ();
j = (cz == 1) ? 13 : 8; /* line spaceing */
color (resb, resb);
for (i = 0; i < n; i++)
atext (wx + 20 / cz, wy + 462 / cz - (i + 1) * j, s[i], 2 - cz);
i = getcur (&x, &y);
if (i < 0)
err ("menu: couldn't read cursor", i, x, y, 0);
color (0, resb);
rect (wx + 20 / cz, wy + 452 / cz - n * j, wx + 300 / cz, wy + 462 / cz);
if (x >= wx + 20 / cz && x < wx + 160 &&
y <= wy + 462 / cz && y > wy + 462 / cz - n * j) {
msg_on ();
return ((wy + 462 / cz - y) / j);}
else {
err_msg ("Nothing selected");
return (-1);
}
}
clp_plt (x0, y0, x1, y1, xl, yl, xh, yh) /* clip plot */
int x0, y0, x1, y1, xl, yl, xh, yh;
{
float a, b, c;
int t;
if (batch) /* redundant */
return;
if (xh < xl || yh < yl ||
((x0 < xl && x1 < xl) || (x0 > xh && x1 > xh)) ||
((y0 < yl && y1 < yl) || (y0 > yh && y1 > yh)))
return; /* nothing to plot */
if (x0 == x1 && y0 == x1) { /* just one point */
point (x0, x1);
return;
}
a = y0 - y1; /* get vector equation */
b = x1 - x0;
c = y0 * x1 - x0 * y1;
if (x0 > x1) { /* insure x0 <= x1 */
t = x0;
x0 = x1;
x1 = t;
t = y0;
y0 = y1;
y1 = t;
}
if (x0 < xl) { /* xl - clip */
x0 = xl;
y0 = (b == 0.0) ? y0 : (c - a * (float) xl) / b;
}
if (x1 > xh) { /* xh - clip */
x1 = xh;
y1 = (b == 0.0) ? y1 : (c - a * (float) xh) / b;
}
if (y0 > y1) { /* insure y0 <= y1 */
t = x0;
x0 = x1;
x1 = t;
t = y0;
y0 = y1;
y1 = t;
}
if (y0 > yh || y1 < yl) /* out of area ? */
return;
if (y0 < yl) { /* yl - clip */
y0 = yl;
x0 = (a == 0.0) ? x0 : (c - b * (float) yl) / a;
}
if (y1 > yh) { /* yh - clip */
y1 = yh;
x1 = (a == 0.0) ? x1 : (c - b * (float) yh) / a;
}
if (x0 == x1 && y0 == x1) /* draw result */
point (x0, x1);
else {
move (x0, y0);
draw (x1, y1);
}
}
aed_plt (x0, y0, x1, y1) /* plot on aed within window */
int x0, y0, x1, y1;
{
int xl, yl, xh, yh;
static int msk = 0xfffffe00;
xl = wx;
xh = wx + 511 / cz;
yl = wy;
yh = wy + 482 / cz;
if ((xl&msk) != (xh&msk)) { /* need to split x */
if ((yl&msk) != (yh&msk)) {/* need to split y */
clp_plt (x0, y0, x1, y1, xl, yl, (xl&msk) + 511, (yl&msk) + 511);
clp_plt (x0, y0, x1, y1, (xl&msk) + 512, yl, xh, (yl&msk) + 511);
clp_plt (x0, y0, x1, y1, xl, (yl&msk) + 512, (xl&msk) + 511, yh);
clp_plt (x0, y0, x1, y1, (xl&msk) + 512, (yl&msk) + 512, xh, yh);}
else {
clp_plt (x0, y0, x1, y1, xl, yl, (xl&msk) + 511, yh);
clp_plt (x0, y0, x1, y1, (xl&msk) + 512, yl, xh, yh);
}}
else {
if ((yl&msk) != (yh&msk)) {/* need to split y */
clp_plt (x0, y0, x1, y1, xl, yl, xh, (yl&msk) + 511);
clp_plt (x0, y0, x1, y1, xl, (yl&msk) + 512, xh, yh);}
else
clp_plt (x0, y0, x1, y1, xl, yl, xh, yh);
}
}
chg_color ()
/************************************************\
* *
* Change color: interactive changing of colors *
* *
\************************************************/
{
register int i, j, bs, bl, x, y;
int ix, iy;
char *ln[ncolors + 1];
struct color_tab t;
ln[0] = "flip sides";
for (i = 0; i < ncolors; i++)
ln[i + 1] = Color_tab[i].name;
if (0 <= (i = menu (ln, ncolors + 1))) { /* change it */
if (!i) { /* just flip s1b/s2b */
t = Color_tab[CT_s1_n];
Color_tab[CT_s1_n] = Color_tab[CT_s2_n];
Color_tab[CT_s2_n] = t;
t = Color_tab[CT_s1_s];
Color_tab[CT_s1_s] = Color_tab[CT_s2_s];
Color_tab[CT_s2_s] = t;
ldcmp ();
return;}
else
i--;
j = (cz == 1) ? 13 : 8; /* line spaceing */
x = wx + 20 / cz; /* x-start point */
y = wy + 462 / cz; /* y-start point */
bs = wx + 128 / cz; /* bar start */
bl = (cz == 1) ? 255 : 127; /* bar length */
msg_off ();
color (resb, resb);
draw_bar (x, y, "Red", Color_tab[i].r, bs, bl, j);
draw_bar (x, y - j, "Green", Color_tab[i].g, bs, bl, j);
draw_bar (x, y - j * 2, "Blue", Color_tab[i].b, bs, bl, j);
while (getcur (&ix, &iy) < 4) { /* change it */
if (ix < bs || ix > (bs + bl))
break;
ix = (ix - bs) * cz;
iy = (iy - (y - 2 * j)) / j;
if (iy < 0 || iy > 2)
break;
switch (iy) {
case 0: /* blue */
Color_tab[i].b = ix;
new_bar (y - 2 * j, ix, bs, bl, j - 3);
break;
case 1: /* Green */
Color_tab[i].g = ix;
new_bar (y - j, ix, bs, bl, j - 3);
break;
case 2: /* red */
Color_tab[i].r = ix;
new_bar (y, ix, bs, bl, j - 3);
break;
}
ldcmp ();
}
color (0, resb); /* clean it */
rect (x, y - 2 * j, x + 384 / cz, y + j);
msg_on ();
}
}
draw_bar (x, y, s, v, bs, bl, dy) /* draw a value bar */
int x, y, v, bs, bl, dy;
char *s;
{
atext (x, y, s, 2 - cz);
new_bar (y, v, bs, bl, dy - 3);
}
new_bar (y, v, bs, bl, dy) /* plain bar */
int y, v, bs, bl, dy;
{
color (resb, resb);
rect (bs, y, bs + bl, y + dy);
v /= cz;
if (v < (bl - 1) && v >= 0) {/* the value (v) should be in [0,255] */
color (0, resb);
rect (bs + v + 1, y + 1, bs + bl - 1, y + dy - 1);
color (resb, resb);
}
}
struct nlst *deq_NL () /* alocate a NL element */
{
register struct nlst *p;
if (V.enll) { /* recycle old ones */
p = V.enll;
V.enll = p -> n;}
else {
if (V.nnl >= nlmax)
err ("Net list space too small: increase 'nlmax'", nlmax, 0, 0,0);
p = &NL[V.nnl++];
}
return p;
}
!E!O!F!
#
# type sh /usru0/agn/pcb/distr/../usenet/part7 to unpack this archive.
#
echo extracting pwide.c...
cat >pwide.c <<'!E!O!F!'
/***************************************************************\
* *
* PCB program *
* *
* Wide wire support stuff *
* *
* (c) 1985 A. Nowatzyk *
* *
\***************************************************************/
#include <stdio.h>
#include "pparm.h"
#include "pcdst.h"
extern char uc_tab[]; /* see pwork: ck_rdnb */
extern int pdr[8]; /* offset direction table */
extern int sp[9][2]; /* search pattern */
extern char drc_tab1[256]; /* drc decision table */
static char **pnt_lst = 0; /* point list */
static int pnt_max = xmax; /* max number of points */
static int pnt_cnt; /* point count */
static int side; /* side to work on */
fnd_pnt (x, y)
int x, y;
/**************************************************************************\
* *
* find points: (similar to fnd_seg of pplow.c) *
* <x,y> must point to a part of a trace on side "side". fnd_pnt dumps *
* the pointer to all bits of that segment on the segment list. The *
* segment is terminated by either the center of a hole, the center *
* of a 'Y' or the last point of a floating end. The terminating *
* point *is* added to the segment list. *
* *
\**************************************************************************/
{
register int i;
if (!pnt_lst) { /* alloacte point list */
pnt_lst = (char **) malloc (sizeof (char *) * pnt_max);
}
pnt_cnt = 0; /* reset point list counter */
if (!(pcb[y][x] & side))
return; /* no start point !! */
for (i = 0; i <= 8; i++) /* find direction */
if (pcb[y + dr[i][1]][x + dr[i][0]] & side)
break;
if (i > 7)
return; /* start on single point ?? */
/* scan one way */
pnt_scan (&pcb[y][x], i, side);
/* scan the other way */
pnt_scan (&pcb[y + dr[i][1]][x + dr[i][0]], (i + 4) & 7, side);
}
pnt_scan (p, d, s)
char *p;
int d, s;
/****************************************************************\
* *
* Point scan: *
* <p> points to the start point. <d> is the direction of the *
* next point. The next point will be added to the list. Scan *
* stops if that point was a terminal, that is the center of *
* a hole, a 'Y' or an floating end. *
* *
\****************************************************************/
{
register char *pp;
register int i, j = d, ss = ~s, k, l;
int m;
pp = p + pdr[j];
do {
if (pnt_cnt >= pnt_max) { /* need more space */
pnt_max += 10 + pnt_max / 2;
pnt_lst = (char **) realloc (pnt_lst, sizeof (char *) * pnt_max);
}
pnt_lst[pnt_cnt++] = pp; /* add point */
i = 0;
if (*(pp + 1) & ~ss) i++;
if (*(pp + xmax + 1) & ~ss) i++;
if (*(pp + xmax ) & ~ss) i++;
if (*(pp + xmax - 1) & ~ss) i++;
if (*(pp - 1) & ~ss) i++;
if (*(pp - xmax - 1) & ~ss) i++;
if (*(pp - xmax ) & ~ss) i++;
if (*(pp - xmax + 1) & ~ss) i++;
if (i > 2) { /* check for a real Y */
for (m = 0; m < 8; m++)
if (*(pp + pdr[m]) & ~ss) {
k = 0;
for (l = 0; l < 8; l++)
if (*(pp + pdr[m] + pdr[l]) & ~ss)
k |= 1 << l;
i -= uc_tab[k];
}
}
if (i != 2) break; /* floating or Y terminal point */
if (*pp & ahb && /* hole terminal point check */
*(pp + 1) & ahb &&
*(pp + xmax + 1) & ahb &&
*(pp + xmax ) & ahb &&
*(pp + xmax - 1) & ahb &&
*(pp - 1) & ahb &&
*(pp - xmax - 1) & ahb &&
*(pp - xmax ) & ahb &&
*(pp - xmax + 1) & ahb ) break;
for (i = 6; i < 11; i++) /* find new direction */
if (*(pp + pdr[(j + i) & 7]) & ~ss)
break;
pp += pdr[j = (j + i) & 7];
} while (i < 11);
}
expand (n) /* widen wire by n */
int n;
{
static int wt5[12] =
{2, xmax + 2, 2 * xmax + 1, 2 * xmax, 2 * xmax - 1, xmax - 2,
-2, -2 - xmax, -1 - 2 * xmax, -2 * xmax, 1 - 2 * xmax, 2 - xmax};
static int *wt[2] = {pdr, wt5};
static int wl[2] = {8, 12};
register int i, j, k, t;
register char *p;
if (n < 0 || n > 1)
return; /* you may want to add 2, 3... */
t = ~side; /* DRC test */
for (i = 0; i < pnt_cnt; i++)
for (j = 0; j < wl[n]; j++) {
p = pnt_lst[i] + wt[n][j]; /* new candidate */
if (*p & side)
continue; /* already taken */
for (k = 0; k < 8; k++) /* check DRC */
if (drc_tab1[*(p + pdr[k]) & 0xff] & ~t)
break;
if (k >= 8) /* ok */
pxl (((int) p - (int) pcb) % xmax,
((int) p - (int) pcb) / xmax);
}
}
wide_wire (ctx) /* wide wire function */
int ctx;
{
struct nlhd *get_net (), *net;
register int i, j;
int x, y;
static char *mtext[] = {
"13 mil (default)",
"38 mil",
"63 mil"
};
err_msg ("Point to wire");
getcur (&x, &y);
for (i = 0; i < 9; i++) /* look for wire */
if (pcb[y + sp[i][1]][x + sp[i][0]] & vec)
break;
if (i >= 9) {
err_msg ("No wire found");
return ctx;
}
x += sp[i][0]; /* update wire pointer */
y += sp[i][1];
side = pcb[y][x] & vec;
if (side == vec)
side = top_side;
for (i = 0, j = 0; i < 8; i++)
if (pcb[y + dr[i][1]][x + dr[i][0]] & side)
j++;
if (j > 2) {
err_msg ("no expand on expanded wire");
return ctx;
}
switch (menu (mtext, 3)) {
case 1:
i = 1; /* 2 raster units + line width */
break;
case 2:
i = 2; /* 4 raster units + line width */
break;
default:
err_msg ("No change made");
return ctx;
}
net = get_net (x, y, side); /* find net */
if (V.cnet != net) { /* select it */
if (V.cnet)
deseln (V.cnet);
selnet (net);
}
fnd_pnt (x, y); /* find the stuff */
color (selb | side, selb | side); /* set up color */
for (j = 0; j < i; j++)
expand (j); /* here we go */
for (i = 0; i < pnt_cnt; i++)
if (*pnt_lst[i] & ahb) { /* clean up holes */
x = ((int) pnt_lst[i] - (int) pcb) % xmax;
y = ((int) pnt_lst[i] - (int) pcb) / xmax;
ckgp (x, y, s1b);
ckgp (x, y, s2b);
}
return net_sel (START);
}
!E!O!F!
More information about the Comp.sources.unix
mailing list