pcb 4.1.1
An interactive printed circuit board layout editor.

src/misc.c

Go to the documentation of this file.
00001 
00035 #ifdef HAVE_CONFIG_H
00036 #include "config.h"
00037 #endif
00038 
00039 #include <stdlib.h>
00040 #include <stdarg.h>
00041 #ifdef HAVE_STRING_H
00042 #include <string.h>
00043 #endif
00044 #include <memory.h>
00045 #include <ctype.h>
00046 #include <signal.h>
00047 #include <sys/param.h>
00048 #include <sys/stat.h>
00049 #include <sys/types.h>
00050 #include <math.h>
00051 #ifdef HAVE_UNISTD_H
00052 #include <unistd.h>
00053 #endif
00054 #ifdef HAVE_PWD_H
00055 #include <pwd.h>
00056 #endif
00057 
00058 #include "global.h"
00059 
00060 #include "box.h"
00061 #include "crosshair.h"
00062 #include "create.h"
00063 #include "data.h"
00064 #include "draw.h"
00065 #include "file.h"
00066 #include "error.h"
00067 #include "mymem.h"
00068 #include "misc.h"
00069 #include "move.h"
00070 #include "pcb-printf.h"
00071 #include "polygon.h"
00072 #include "remove.h"
00073 #include "rtree.h"
00074 #include "rotate.h"
00075 #include "rubberband.h"
00076 #include "search.h"
00077 #include "set.h"
00078 #include "undo.h"
00079 #include "action.h"
00080 
00081 #ifdef HAVE_LIBDMALLOC
00082 #include <dmalloc.h>
00083 #endif
00084 
00085 /*      forward declarations    */
00086 static char *BumpName (char *);
00087 static void GetGridLockCoordinates (int, void *, void *, void *,
00088                                     Coord *, Coord *);
00089 
00090 
00091 /* Local variables */
00092 
00097 static struct
00098 {
00099   bool ElementOn, InvisibleObjectsOn, PinOn, ViaOn, RatOn;
00100   int LayerStack[MAX_ALL_LAYER];
00101   bool LayerOn[MAX_ALL_LAYER];
00102   int cnt;
00103 } SavedStack;
00104 
00109 double
00110 Distance (double x1, double y1, double x2, double y2)
00111 {
00112   return hypot(x2 - x1, y2 - y1);
00113 }
00114 
00118 Angle
00119 NormalizeAngle (Angle a)
00120 {
00121   while (a < 0)
00122     a += 360.0;
00123   while (a >= 360.0)
00124     a -= 360.0;
00125   return a;
00126 }
00127 
00133 double
00134 GetValue (const char *val, const char *units, bool * absolute)
00135 {
00136   return GetValueEx(val, units, absolute, NULL, "cmil");
00137 }
00138 
00139 double
00140 GetValueEx (const char *val, const char *units, bool * absolute, UnitList extra_units, const char *default_unit)
00141 {
00142   double value;
00143   int n = -1;
00144   bool scaled = 0;
00145   bool dummy;
00146 
00147   /* Allow NULL to be passed for absolute */
00148   if(absolute == NULL)
00149     absolute = &dummy;
00150 
00151   /* if the first character is a sign we have to add the
00152    * value to the current one
00153    */
00154   if (*val == '=')
00155     {
00156       *absolute = true;
00157       if (sscanf (val+1, "%lf%n", &value, &n) < 1)
00158         return 0;
00159       n++;
00160     }
00161   else
00162     {
00163       if (isdigit ((int) *val))
00164         *absolute = true;
00165       else
00166         *absolute = false;
00167       if (sscanf (val, "%lf%n", &value, &n) < 1)
00168         return 0;
00169     }
00170   if (!units && n > 0)
00171     units = val + n;
00172 
00173   while (units && *units == ' ')
00174     units ++;
00175     
00176   if (units && *units)
00177     {
00178       int i;
00179       const Unit *unit = get_unit_struct (units);
00180       if (unit != NULL)
00181         {
00182           value  = unit_to_coord (unit, value);
00183           scaled = 1;
00184         }
00185       if (extra_units)
00186         {
00187           for (i = 0; *extra_units[i].suffix; ++i)
00188             {
00189               if (strncmp (units, extra_units[i].suffix, strlen(extra_units[i].suffix)) == 0)
00190                 {
00191                   value *= extra_units[i].scale;
00192                   if (extra_units[i].flags & UNIT_PERCENT)
00193                     value /= 100.0;
00194                   scaled = 1;
00195                 }
00196             }
00197         }
00198     }
00199   /* Apply default unit */
00200   if (!scaled && default_unit && *default_unit)
00201     {
00202       int i;
00203       const Unit *unit = get_unit_struct (default_unit);
00204       if (extra_units)
00205         for (i = 0; *extra_units[i].suffix; ++i)
00206           if (strcmp (extra_units[i].suffix, default_unit) == 0)
00207             {
00208               value *= extra_units[i].scale;
00209               if (extra_units[i].flags & UNIT_PERCENT)
00210                 value /= 100.0;
00211               scaled = 1;
00212             }
00213       if (!scaled && unit != NULL)
00214         value = unit_to_coord (unit, value);
00215     }
00216 
00217   return value;
00218 }
00219 
00233 double GetUnitlessValue (const char *val, bool *absolute) {
00234   double value;
00235 
00236   if (*val == '=')
00237     {
00238       *absolute = true;
00239       val++;
00240     }
00241   else
00242     {
00243       if (isdigit ((int) *val))
00244         *absolute = true;
00245       else
00246         *absolute = false;
00247     }
00248 
00249   if (sscanf (val, "%lf", &value) < 1)
00250     return 0.;
00251 
00252   return value;
00253 }
00254 
00258 void
00259 SetPointBoundingBox (PointType *Pnt)
00260 {
00261   Pnt->X2 = Pnt->X + 1;
00262   Pnt->Y2 = Pnt->Y + 1;
00263 }
00264 
00268 void
00269 SetPinBoundingBox (PinType *Pin)
00270 {
00271   Coord width;
00272 
00273   /* the bounding box covers the extent of influence
00274    * so it must include the clearance values too
00275    */
00276   width = MAX (Pin->Clearance + PIN_SIZE (Pin), Pin->Mask) / 2;
00277 
00278   /* Adjust for our discrete polygon approximation */
00279   width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
00280 
00281   Pin->BoundingBox.X1 = Pin->X - width;
00282   Pin->BoundingBox.Y1 = Pin->Y - width;
00283   Pin->BoundingBox.X2 = Pin->X + width;
00284   Pin->BoundingBox.Y2 = Pin->Y + width;
00285   close_box(&Pin->BoundingBox);
00286 }
00287 
00291 void
00292 SetPadBoundingBox (PadType *Pad)
00293 {
00294   Coord width;
00295   Coord deltax;
00296   Coord deltay;
00297 
00298   /* the bounding box covers the extent of influence
00299    * so it must include the clearance values too
00300    */
00301   width = (Pad->Thickness + Pad->Clearance + 1) / 2;
00302   width = MAX (width, (Pad->Mask + 1) / 2);
00303   deltax = Pad->Point2.X - Pad->Point1.X;
00304   deltay = Pad->Point2.Y - Pad->Point1.Y;
00305 
00306   if (TEST_FLAG (SQUAREFLAG, Pad) && deltax != 0 && deltay != 0)
00307     {
00308       /* slanted square pad */
00309       double theta;
00310       Coord btx, bty;
00311 
00312       /* T is a vector half a thickness long, in the direction of
00313           one of the corners.  */
00314       theta = atan2 (deltay, deltax);
00315       btx = width * cos (theta + M_PI/4) * sqrt(2.0);
00316       bty = width * sin (theta + M_PI/4) * sqrt(2.0);
00317 
00318 
00319       Pad->BoundingBox.X1 = MIN (MIN (Pad->Point1.X - btx, Pad->Point1.X - bty),
00320                                  MIN (Pad->Point2.X + btx, Pad->Point2.X + bty));
00321       Pad->BoundingBox.X2 = MAX (MAX (Pad->Point1.X - btx, Pad->Point1.X - bty),
00322                                  MAX (Pad->Point2.X + btx, Pad->Point2.X + bty));
00323       Pad->BoundingBox.Y1 = MIN (MIN (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
00324                                  MIN (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
00325       Pad->BoundingBox.Y2 = MAX (MAX (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
00326                                  MAX (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
00327     }
00328   else
00329     {
00330       /* Adjust for our discrete polygon approximation */
00331       width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
00332 
00333       Pad->BoundingBox.X1 = MIN (Pad->Point1.X, Pad->Point2.X) - width;
00334       Pad->BoundingBox.X2 = MAX (Pad->Point1.X, Pad->Point2.X) + width;
00335       Pad->BoundingBox.Y1 = MIN (Pad->Point1.Y, Pad->Point2.Y) - width;
00336       Pad->BoundingBox.Y2 = MAX (Pad->Point1.Y, Pad->Point2.Y) + width;
00337     }
00338   close_box(&Pad->BoundingBox);
00339 }
00340 
00344 void
00345 SetLineBoundingBox (LineType *Line)
00346 {
00347   Coord width = (Line->Thickness + Line->Clearance + 1) / 2;
00348 
00349   /* Adjust for our discrete polygon approximation */
00350   width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
00351 
00352   Line->BoundingBox.X1 = MIN (Line->Point1.X, Line->Point2.X) - width;
00353   Line->BoundingBox.X2 = MAX (Line->Point1.X, Line->Point2.X) + width;
00354   Line->BoundingBox.Y1 = MIN (Line->Point1.Y, Line->Point2.Y) - width;
00355   Line->BoundingBox.Y2 = MAX (Line->Point1.Y, Line->Point2.Y) + width;
00356   close_box(&Line->BoundingBox);
00357   SetPointBoundingBox (&Line->Point1);
00358   SetPointBoundingBox (&Line->Point2);
00359 }
00360 
00364 void
00365 SetPolygonBoundingBox (PolygonType *Polygon)
00366 {
00367   Polygon->BoundingBox.X1 = Polygon->BoundingBox.Y1 = MAX_COORD;
00368   Polygon->BoundingBox.X2 = Polygon->BoundingBox.Y2 = 0;
00369   POLYGONPOINT_LOOP (Polygon);
00370   {
00371     MAKEMIN (Polygon->BoundingBox.X1, point->X);
00372     MAKEMIN (Polygon->BoundingBox.Y1, point->Y);
00373     MAKEMAX (Polygon->BoundingBox.X2, point->X);
00374     MAKEMAX (Polygon->BoundingBox.Y2, point->Y);
00375   }
00376   /* boxes don't include the lower right corner */
00377   close_box(&Polygon->BoundingBox);
00378   END_LOOP;
00379 }
00380 
00384 void
00385 SetElementBoundingBox (DataType *Data, ElementType *Element,
00386                        FontType *Font)
00387 {
00388   BoxType *box, *vbox;
00389 
00390   if (Data && Data->element_tree)
00391     r_delete_entry (Data->element_tree, (BoxType *) Element);
00392   /* first update the text objects */
00393   ELEMENTTEXT_LOOP (Element);
00394   {
00395     if (Data && Data->name_tree[n])
00396       r_delete_entry (Data->name_tree[n], (BoxType *) text);
00397     SetTextBoundingBox (Font, text);
00398     if (Data && !Data->name_tree[n])
00399       Data->name_tree[n] = r_create_tree (NULL, 0, 0);
00400     if (Data)
00401       r_insert_entry (Data->name_tree[n], (BoxType *) text, 0);
00402   }
00403   END_LOOP;
00404 
00405   /* do not include the elementnames bounding box which
00406    * is handled separately
00407    */
00408   box = &Element->BoundingBox;
00409   vbox = &Element->VBox;
00410   box->X1 = box->Y1 = MAX_COORD;
00411   box->X2 = box->Y2 = 0;
00412   ELEMENTLINE_LOOP (Element);
00413   {
00414     SetLineBoundingBox (line);
00415     MAKEMIN (box->X1, line->Point1.X - (line->Thickness + 1) / 2);
00416     MAKEMIN (box->Y1, line->Point1.Y - (line->Thickness + 1) / 2);
00417     MAKEMIN (box->X1, line->Point2.X - (line->Thickness + 1) / 2);
00418     MAKEMIN (box->Y1, line->Point2.Y - (line->Thickness + 1) / 2);
00419     MAKEMAX (box->X2, line->Point1.X + (line->Thickness + 1) / 2);
00420     MAKEMAX (box->Y2, line->Point1.Y + (line->Thickness + 1) / 2);
00421     MAKEMAX (box->X2, line->Point2.X + (line->Thickness + 1) / 2);
00422     MAKEMAX (box->Y2, line->Point2.Y + (line->Thickness + 1) / 2);
00423   }
00424   END_LOOP;
00425   ARC_LOOP (Element);
00426   {
00427     SetArcBoundingBox (arc);
00428     MAKEMIN (box->X1, arc->BoundingBox.X1);
00429     MAKEMIN (box->Y1, arc->BoundingBox.Y1);
00430     MAKEMAX (box->X2, arc->BoundingBox.X2);
00431     MAKEMAX (box->Y2, arc->BoundingBox.Y2);
00432   }
00433   END_LOOP;
00434   *vbox = *box;
00435   PIN_LOOP (Element);
00436   {
00437     if (Data && Data->pin_tree)
00438       r_delete_entry (Data->pin_tree, (BoxType *) pin);
00439     SetPinBoundingBox (pin);
00440     if (Data)
00441       {
00442         if (!Data->pin_tree)
00443           Data->pin_tree = r_create_tree (NULL, 0, 0);
00444         r_insert_entry (Data->pin_tree, (BoxType *) pin, 0);
00445       }
00446     MAKEMIN (box->X1, pin->BoundingBox.X1);
00447     MAKEMIN (box->Y1, pin->BoundingBox.Y1);
00448     MAKEMAX (box->X2, pin->BoundingBox.X2);
00449     MAKEMAX (box->Y2, pin->BoundingBox.Y2);
00450     MAKEMIN (vbox->X1, pin->X - pin->Thickness / 2);
00451     MAKEMIN (vbox->Y1, pin->Y - pin->Thickness / 2);
00452     MAKEMAX (vbox->X2, pin->X + pin->Thickness / 2);
00453     MAKEMAX (vbox->Y2, pin->Y + pin->Thickness / 2);
00454   }
00455   END_LOOP;
00456   PAD_LOOP (Element);
00457   {
00458     if (Data && Data->pad_tree)
00459       r_delete_entry (Data->pad_tree, (BoxType *) pad);
00460     SetPadBoundingBox (pad);
00461     if (Data)
00462       {
00463         if (!Data->pad_tree)
00464           Data->pad_tree = r_create_tree (NULL, 0, 0);
00465         r_insert_entry (Data->pad_tree, (BoxType *) pad, 0);
00466       }
00467     MAKEMIN (box->X1, pad->BoundingBox.X1);
00468     MAKEMIN (box->Y1, pad->BoundingBox.Y1);
00469     MAKEMAX (box->X2, pad->BoundingBox.X2);
00470     MAKEMAX (box->Y2, pad->BoundingBox.Y2);
00471     MAKEMIN (vbox->X1,
00472              MIN (pad->Point1.X, pad->Point2.X) - pad->Thickness / 2);
00473     MAKEMIN (vbox->Y1,
00474              MIN (pad->Point1.Y, pad->Point2.Y) - pad->Thickness / 2);
00475     MAKEMAX (vbox->X2,
00476              MAX (pad->Point1.X, pad->Point2.X) + pad->Thickness / 2);
00477     MAKEMAX (vbox->Y2,
00478              MAX (pad->Point1.Y, pad->Point2.Y) + pad->Thickness / 2);
00479   }
00480   END_LOOP;
00481   /* now we set the EDGE2FLAG of the pad if Point2
00482    * is closer to the outside edge than Point1
00483    */
00484   PAD_LOOP (Element);
00485   {
00486     if (pad->Point1.Y == pad->Point2.Y)
00487       {
00488         /* horizontal pad */
00489         if (box->X2 - pad->Point2.X < pad->Point1.X - box->X1)
00490           SET_FLAG (EDGE2FLAG, pad);
00491         else
00492           CLEAR_FLAG (EDGE2FLAG, pad);
00493       }
00494     else
00495       {
00496         /* vertical pad */
00497         if (box->Y2 - pad->Point2.Y < pad->Point1.Y - box->Y1)
00498           SET_FLAG (EDGE2FLAG, pad);
00499         else
00500           CLEAR_FLAG (EDGE2FLAG, pad);
00501       }
00502   }
00503   END_LOOP;
00504 
00505   /* mark pins with component orientation */
00506   if ((box->X2 - box->X1) > (box->Y2 - box->Y1))
00507     {
00508       PIN_LOOP (Element);
00509       {
00510         SET_FLAG (EDGE2FLAG, pin);
00511       }
00512       END_LOOP;
00513     }
00514   else
00515     {
00516       PIN_LOOP (Element);
00517       {
00518         CLEAR_FLAG (EDGE2FLAG, pin);
00519       }
00520       END_LOOP;
00521     }
00522   close_box(box);
00523   close_box(vbox);
00524   if (Data && !Data->element_tree)
00525     Data->element_tree = r_create_tree (NULL, 0, 0);
00526   if (Data)
00527     r_insert_entry (Data->element_tree, box, 0);
00528 }
00529 
00533 void
00534 SetTextBoundingBox (FontType *FontPtr, TextType *Text)
00535 {
00536   SymbolType *symbol = FontPtr->Symbol;
00537   unsigned char *s = (unsigned char *) Text->TextString;
00538   int i;
00539   int space;
00540 
00541   Coord minx, miny, maxx, maxy, tx;
00542   Coord min_final_radius;
00543   Coord min_unscaled_radius;
00544   bool first_time = true;
00545 
00546   minx = miny = maxx = maxy = tx = 0;
00547 
00548   /* Calculate the bounding box based on the larger of the thicknesses
00549    * the text might clamped at on silk or copper layers.
00550    */
00551   min_final_radius = MAX (PCB->minWid, PCB->minSlk) / 2;
00552 
00553   /* Pre-adjust the line radius for the fact we are initially computing the
00554    * bounds of the un-scaled text, and the thickness clamping applies to
00555    * scaled text.
00556    */
00557   min_unscaled_radius = UNSCALE_TEXT (min_final_radius, Text->Scale);
00558 
00559   /* calculate size of the bounding box */
00560   for (; s && *s; s++)
00561     {
00562       if (*s <= MAX_FONTPOSITION && symbol[*s].Valid)
00563         {
00564           LineType *line = symbol[*s].Line;
00565           for (i = 0; i < symbol[*s].LineN; line++, i++)
00566             {
00567               /* Clamp the width of text lines at the minimum thickness.
00568                * NB: Divide 4 in thickness calculation is comprised of a factor
00569                *     of 1/2 to get a radius from the center-line, and a factor
00570                *     of 1/2 because some stupid reason we render our glyphs
00571                *     at half their defined stroke-width.
00572                */
00573                Coord unscaled_radius = MAX (min_unscaled_radius, line->Thickness / 4);
00574 
00575               if (first_time)
00576                 {
00577                   minx = maxx = line->Point1.X;
00578                   miny = maxy = line->Point1.Y;
00579                   first_time = false;
00580                 }
00581 
00582               minx = MIN (minx, line->Point1.X - unscaled_radius + tx);
00583               miny = MIN (miny, line->Point1.Y - unscaled_radius);
00584               minx = MIN (minx, line->Point2.X - unscaled_radius + tx);
00585               miny = MIN (miny, line->Point2.Y - unscaled_radius);
00586               maxx = MAX (maxx, line->Point1.X + unscaled_radius + tx);
00587               maxy = MAX (maxy, line->Point1.Y + unscaled_radius);
00588               maxx = MAX (maxx, line->Point2.X + unscaled_radius + tx);
00589               maxy = MAX (maxy, line->Point2.Y + unscaled_radius);
00590             }
00591           space = symbol[*s].Delta;
00592         }
00593       else
00594         {
00595           BoxType *ds = &FontPtr->DefaultSymbol;
00596           Coord w = ds->X2 - ds->X1;
00597 
00598           minx = MIN (minx, ds->X1 + tx);
00599           miny = MIN (miny, ds->Y1);
00600           minx = MIN (minx, ds->X2 + tx);
00601           miny = MIN (miny, ds->Y2);
00602           maxx = MAX (maxx, ds->X1 + tx);
00603           maxy = MAX (maxy, ds->Y1);
00604           maxx = MAX (maxx, ds->X2 + tx);
00605           maxy = MAX (maxy, ds->Y2);
00606 
00607           space = w / 5;
00608         }
00609       tx += symbol[*s].Width + space;
00610     }
00611 
00612   /* scale values */
00613   minx = SCALE_TEXT (minx, Text->Scale);
00614   miny = SCALE_TEXT (miny, Text->Scale);
00615   maxx = SCALE_TEXT (maxx, Text->Scale);
00616   maxy = SCALE_TEXT (maxy, Text->Scale);
00617 
00618   /* set upper-left and lower-right corner;
00619    * swap coordinates if necessary (origin is already in 'swapped')
00620    * and rotate box
00621    */
00622 
00623   if (TEST_FLAG (ONSOLDERFLAG, Text))
00624     {
00625       Text->BoundingBox.X1 = Text->X + minx;
00626       Text->BoundingBox.Y1 = Text->Y - miny;
00627       Text->BoundingBox.X2 = Text->X + maxx;
00628       Text->BoundingBox.Y2 = Text->Y - maxy;
00629       RotateBoxLowLevel (&Text->BoundingBox, Text->X, Text->Y,
00630                          (4 - Text->Direction) & 0x03);
00631     }
00632   else
00633     {
00634       Text->BoundingBox.X1 = Text->X + minx;
00635       Text->BoundingBox.Y1 = Text->Y + miny;
00636       Text->BoundingBox.X2 = Text->X + maxx;
00637       Text->BoundingBox.Y2 = Text->Y + maxy;
00638       RotateBoxLowLevel (&Text->BoundingBox,
00639                          Text->X, Text->Y, Text->Direction);
00640     }
00641 
00642   /* the bounding box covers the extent of influence
00643    * so it must include the clearance values too
00644    */
00645   Text->BoundingBox.X1 -= PCB->Bloat;
00646   Text->BoundingBox.Y1 -= PCB->Bloat;
00647   Text->BoundingBox.X2 += PCB->Bloat;
00648   Text->BoundingBox.Y2 += PCB->Bloat;
00649   close_box(&Text->BoundingBox);
00650 }
00651 
00655 bool
00656 IsDataEmpty (DataType *Data)
00657 {
00658   bool hasNoObjects;
00659   Cardinal i;
00660 
00661   hasNoObjects = (Data->ViaN == 0);
00662   hasNoObjects &= (Data->ElementN == 0);
00663   for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
00664     hasNoObjects = hasNoObjects &&
00665       Data->Layer[i].LineN == 0 &&
00666       Data->Layer[i].ArcN == 0 &&
00667       Data->Layer[i].TextN == 0 && Data->Layer[i].PolygonN == 0;
00668   return (hasNoObjects);
00669 }
00670 
00671 int
00672 FlagIsDataEmpty (int parm)
00673 {
00674   int i = IsDataEmpty (PCB->Data);
00675   return parm ? !i : i;
00676 }
00677 
00678 /* FLAG(DataEmpty,FlagIsDataEmpty,0) */
00679 /* FLAG(DataNonEmpty,FlagIsDataEmpty,1) */
00680 
00681 bool
00682 IsLayerEmpty (LayerType *layer)
00683 {
00684   return (layer->LineN == 0
00685           && layer->TextN == 0
00686           && layer->PolygonN == 0
00687           && layer->ArcN == 0);
00688 }
00689 
00690 bool
00691 IsLayerNumEmpty (int num)
00692 {
00693   return IsLayerEmpty (PCB->Data->Layer+num);
00694 }
00695 
00696 bool
00697 IsLayerGroupEmpty (int num)
00698 {
00699   int i;
00700   for (i=0; i<PCB->LayerGroups.Number[num]; i++)
00701     if (!IsLayerNumEmpty (PCB->LayerGroups.Entries[num][i]))
00702       return false;
00703   return true;
00704 }
00705 
00706 bool
00707 IsPasteEmpty (int side)
00708 {
00709   bool paste_empty = true;
00710   ALLPAD_LOOP (PCB->Data);
00711   {
00712     if (ON_SIDE (pad, side) && !TEST_FLAG (NOPASTEFLAG, pad) && pad->Mask > 0)
00713       {
00714         paste_empty = false;
00715         break;
00716       }
00717   }
00718   ENDALL_LOOP;
00719   return paste_empty;
00720 }
00721 
00722 
00723 typedef struct
00724 {
00725   int nplated;
00726   int nunplated;
00727   Cardinal group_from;
00728   Cardinal group_to;
00729 } HoleCountStruct;
00730 
00731 static int
00732 hole_counting_callback (const BoxType * b, void *cl)
00733 {
00734   PinType *pin = (PinType *) b;
00735   HoleCountStruct *hcs = (HoleCountStruct *) cl;
00736 
00737   if (hcs->group_from != 0
00738           || hcs->group_to != 0)
00739     {
00740       if (VIA_IS_BURIED (pin))
00741         {
00742           if (hcs->group_from == GetLayerGroupNumberByNumber (pin->BuriedFrom)
00743               && hcs->group_to == GetLayerGroupNumberByNumber (pin->BuriedTo))
00744             goto count;
00745         }
00746       return 1;
00747     }
00748 
00749 count:
00750   if (TEST_FLAG (HOLEFLAG, pin))
00751     hcs->nunplated++;
00752   else
00753     hcs->nplated++;
00754   return 1;
00755 }
00756 
00763 void
00764 CountHoles (int *plated, int *unplated, const BoxType *within_area)
00765 {
00766   HoleCountStruct hcs = {0, 0, 0, 0};
00767 
00768   r_search (PCB->Data->pin_tree, within_area, NULL, hole_counting_callback, &hcs);
00769   r_search (PCB->Data->via_tree, within_area, NULL, hole_counting_callback, &hcs);
00770 
00771   if (plated != NULL)   *plated   = hcs.nplated;
00772   if (unplated != NULL) *unplated = hcs.nunplated;
00773 }
00774 
00781 void
00782 CountHolesEx (int *plated, int *unplated, const BoxType *within_area, Cardinal group_from, Cardinal group_to)
00783 {
00784   HoleCountStruct hcs = {0, 0, group_from, group_to};
00785 
00786   r_search (PCB->Data->pin_tree, within_area, NULL, hole_counting_callback, &hcs);
00787   r_search (PCB->Data->via_tree, within_area, NULL, hole_counting_callback, &hcs);
00788 
00789   if (plated != NULL)   *plated   = hcs.nplated;
00790   if (unplated != NULL) *unplated = hcs.nunplated;
00791 }
00792 
00798 BoxType *
00799 GetDataBoundingBox (DataType *Data)
00800 {
00801   static BoxType box;
00802   /* FIX ME: use r_search to do this much faster */
00803 
00804   /* preset identifiers with highest and lowest possible values */
00805   box.X1 = box.Y1 = MAX_COORD;
00806   box.X2 = box.Y2 = -MAX_COORD;
00807 
00808   /* now scan for the lowest/highest X and Y coordinate */
00809   VIA_LOOP (Data);
00810   {
00811     box.X1 = MIN (box.X1, via->X - via->Thickness / 2);
00812     box.Y1 = MIN (box.Y1, via->Y - via->Thickness / 2);
00813     box.X2 = MAX (box.X2, via->X + via->Thickness / 2);
00814     box.Y2 = MAX (box.Y2, via->Y + via->Thickness / 2);
00815   }
00816   END_LOOP;
00817   ELEMENT_LOOP (Data);
00818   {
00819     box.X1 = MIN (box.X1, element->BoundingBox.X1);
00820     box.Y1 = MIN (box.Y1, element->BoundingBox.Y1);
00821     box.X2 = MAX (box.X2, element->BoundingBox.X2);
00822     box.Y2 = MAX (box.Y2, element->BoundingBox.Y2);
00823     {
00824       TextType *text = &NAMEONPCB_TEXT (element);
00825       box.X1 = MIN (box.X1, text->BoundingBox.X1);
00826       box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
00827       box.X2 = MAX (box.X2, text->BoundingBox.X2);
00828       box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
00829     };
00830   }
00831   END_LOOP;
00832   ALLLINE_LOOP (Data);
00833   {
00834     box.X1 = MIN (box.X1, line->Point1.X - line->Thickness / 2);
00835     box.Y1 = MIN (box.Y1, line->Point1.Y - line->Thickness / 2);
00836     box.X1 = MIN (box.X1, line->Point2.X - line->Thickness / 2);
00837     box.Y1 = MIN (box.Y1, line->Point2.Y - line->Thickness / 2);
00838     box.X2 = MAX (box.X2, line->Point1.X + line->Thickness / 2);
00839     box.Y2 = MAX (box.Y2, line->Point1.Y + line->Thickness / 2);
00840     box.X2 = MAX (box.X2, line->Point2.X + line->Thickness / 2);
00841     box.Y2 = MAX (box.Y2, line->Point2.Y + line->Thickness / 2);
00842   }
00843   ENDALL_LOOP;
00844   ALLARC_LOOP (Data);
00845   {
00846     box.X1 = MIN (box.X1, arc->BoundingBox.X1);
00847     box.Y1 = MIN (box.Y1, arc->BoundingBox.Y1);
00848     box.X2 = MAX (box.X2, arc->BoundingBox.X2);
00849     box.Y2 = MAX (box.Y2, arc->BoundingBox.Y2);
00850   }
00851   ENDALL_LOOP;
00852   ALLTEXT_LOOP (Data);
00853   {
00854     box.X1 = MIN (box.X1, text->BoundingBox.X1);
00855     box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
00856     box.X2 = MAX (box.X2, text->BoundingBox.X2);
00857     box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
00858   }
00859   ENDALL_LOOP;
00860   ALLPOLYGON_LOOP (Data);
00861   {
00862     box.X1 = MIN (box.X1, polygon->BoundingBox.X1);
00863     box.Y1 = MIN (box.Y1, polygon->BoundingBox.Y1);
00864     box.X2 = MAX (box.X2, polygon->BoundingBox.X2);
00865     box.Y2 = MAX (box.Y2, polygon->BoundingBox.Y2);
00866   }
00867   ENDALL_LOOP;
00868   return (IsDataEmpty (Data) ? NULL : &box);
00869 }
00870 
00877 void
00878 CenterDisplay (Coord X, Coord Y, bool warp_pointer)
00879 {
00880   Coord save_grid = PCB->Grid;
00881 
00882   PCB->Grid = 1;
00883 
00884   if (MoveCrosshairAbsolute (X, Y))
00885     notify_crosshair_change (true);
00886 
00887   if (warp_pointer)
00888     gui->set_crosshair (Crosshair.X, Crosshair.Y,
00889                         HID_SC_CENTER_IN_VIEWPORT_AND_WARP_POINTER );
00890   else
00891     gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_CENTER_IN_VIEWPORT);
00892 
00893   PCB->Grid = save_grid;
00894 }
00895 
00903 void
00904 SetFontInfo (FontType *Ptr)
00905 {
00906   Cardinal i, j;
00907   SymbolType *symbol;
00908   LineType *line;
00909   Coord totalminy = MAX_COORD;
00910 
00911   /* calculate cell with and height (is at least DEFAULT_CELLSIZE)
00912    * maximum cell width and height
00913    * minimum x and y position of all lines
00914    */
00915   Ptr->MaxWidth = DEFAULT_CELLSIZE;
00916   Ptr->MaxHeight = DEFAULT_CELLSIZE;
00917   for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
00918     {
00919       Coord minx, miny, maxx, maxy;
00920 
00921       /* next one if the index isn't used or symbol is empty (SPACE) */
00922       if (!symbol->Valid || !symbol->LineN)
00923         continue;
00924 
00925       minx = miny = MAX_COORD;
00926       maxx = maxy = 0;
00927       for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
00928         {
00929           minx = MIN (minx, line->Point1.X);
00930           miny = MIN (miny, line->Point1.Y);
00931           minx = MIN (minx, line->Point2.X);
00932           miny = MIN (miny, line->Point2.Y);
00933           maxx = MAX (maxx, line->Point1.X);
00934           maxy = MAX (maxy, line->Point1.Y);
00935           maxx = MAX (maxx, line->Point2.X);
00936           maxy = MAX (maxy, line->Point2.Y);
00937         }
00938 
00939       /* move symbol to left edge */
00940       for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
00941         MOVE_LINE_LOWLEVEL (line, -minx, 0);
00942 
00943       /* set symbol bounding box with a minimum cell size of (1,1) */
00944       symbol->Width = maxx - minx + 1;
00945       symbol->Height = maxy + 1;
00946 
00947       /* check total min/max  */
00948       Ptr->MaxWidth = MAX (Ptr->MaxWidth, symbol->Width);
00949       Ptr->MaxHeight = MAX (Ptr->MaxHeight, symbol->Height);
00950       totalminy = MIN (totalminy, miny);
00951     }
00952 
00953   /* move coordinate system to the upper edge (lowest y on screen) */
00954   for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
00955     if (symbol->Valid)
00956       {
00957         symbol->Height -= totalminy;
00958         for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
00959           MOVE_LINE_LOWLEVEL (line, 0, -totalminy);
00960       }
00961 
00962   /* setup the box for the default symbol */
00963   Ptr->DefaultSymbol.X1 = Ptr->DefaultSymbol.Y1 = 0;
00964   Ptr->DefaultSymbol.X2 = Ptr->DefaultSymbol.X1 + Ptr->MaxWidth;
00965   Ptr->DefaultSymbol.Y2 = Ptr->DefaultSymbol.Y1 + Ptr->MaxHeight;
00966 }
00967 
00968 static Coord
00969 GetNum (char **s, const char *default_unit)
00970 {
00971   /* Read value */
00972   Coord ret_val = GetValueEx (*s, NULL, NULL, NULL, default_unit);
00973   /* Advance pointer */
00974   while(isalnum(**s) || **s == '.')
00975      (*s)++;
00976   return ret_val;
00977 }
00978 
00986 char *
00987 make_route_string (RouteStyleType rs[], int n_styles)
00988 {
00989   GString *str = g_string_new ("");
00990   gint i;
00991 
00992   for (i = 0; i < n_styles; ++i)
00993     {
00994       char *r_string = pcb_g_strdup_printf ("%s,%$mr,%$mr,%$mr,%$mr", rs[i].Name,
00995                                             rs[i].Thick, rs[i].Diameter,
00996                                             rs[i].Hole, rs[i].Keepaway);
00997       if (i > 0)
00998         g_string_append_c (str, ':');
00999       g_string_append (str, r_string);
01000       g_free (r_string);
01001     }
01002   return g_string_free (str, FALSE);
01003 }
01004 
01013 int
01014 ParseRouteString (char *s, RouteStyleType *routeStyle, const char *default_unit)
01015 {
01016   int i, style;
01017   char Name[256];
01018 
01019   memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
01020   for (style = 0; style < NUM_STYLES; style++, routeStyle++)
01021     {
01022       while (*s && isspace ((int) *s))
01023         s++;
01024       for (i = 0; *s && *s != ','; i++)
01025         Name[i] = *s++;
01026       Name[i] = '\0';
01027       routeStyle->Name = strdup (Name);
01028       if (!isdigit ((int) *++s))
01029         goto error;
01030       routeStyle->Thick = GetNum (&s, default_unit);
01031       while (*s && isspace ((int) *s))
01032         s++;
01033       if (*s++ != ',')
01034         goto error;
01035       while (*s && isspace ((int) *s))
01036         s++;
01037       if (!isdigit ((int) *s))
01038         goto error;
01039       routeStyle->Diameter = GetNum (&s, default_unit);
01040       while (*s && isspace ((int) *s))
01041         s++;
01042       if (*s++ != ',')
01043         goto error;
01044       while (*s && isspace ((int) *s))
01045         s++;
01046       if (!isdigit ((int) *s))
01047         goto error;
01048       routeStyle->Hole = GetNum (&s, default_unit);
01049       /* for backwards-compatibility, we use a 10-mil default
01050        * for styles which omit the keepaway specification. */
01051       if (*s != ',')
01052         routeStyle->Keepaway = MIL_TO_COORD(10);
01053       else
01054         {
01055           s++;
01056           while (*s && isspace ((int) *s))
01057             s++;
01058           if (!isdigit ((int) *s))
01059             goto error;
01060           routeStyle->Keepaway = GetNum (&s, default_unit);
01061           while (*s && isspace ((int) *s))
01062             s++;
01063         }
01064       if (style < NUM_STYLES - 1)
01065         {
01066           while (*s && isspace ((int) *s))
01067             s++;
01068           if (*s++ != ':')
01069             goto error;
01070         }
01071     }
01072   return (0);
01073 
01074 error:
01075   memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
01076   return (1);
01077 }
01078 
01086 int
01087 ParseGroupString (char *group_string, LayerGroupType *LayerGroup, int *LayerN)
01088 {
01089   char *s;
01090   int group, member, layer;
01091   bool c_set = false,        /* flags for the two special layers to */
01092     s_set = false;              /* provide a default setting for old formats */
01093   int groupnum[MAX_ALL_LAYER];
01094 
01095   *LayerN = 0;
01096 
01097   /* Deterimine the maximum layer number */
01098   for (s = group_string; s && *s; s++)
01099     {
01100       while (*s && isspace ((int) *s))
01101         s++;
01102 
01103       switch (*s)
01104         {
01105         case 'c':
01106         case 'C':
01107         case 't':
01108         case 'T':
01109         case 's':
01110         case 'S':
01111         case 'b':
01112         case 'B':
01113           break;
01114 
01115         default:
01116           if (!isdigit ((int) *s))
01117             goto error;
01118           *LayerN = MAX (*LayerN, atoi (s));
01119           break;
01120         }
01121 
01122       while (*++s && isdigit ((int) *s));
01123 
01124       /* ignore white spaces and check for separator */
01125       while (*s && isspace ((int) *s))
01126         s++;
01127 
01128       if (*s == '\0')
01129         break;
01130 
01131       if (*s != ':' && *s != ',')
01132         goto error;
01133     }
01134 
01135   /* clear struct */
01136   memset (LayerGroup, 0, sizeof (LayerGroupType));
01137 
01138   /* Clear assignments */
01139   for (layer = 0; layer < MAX_ALL_LAYER; layer++)
01140     groupnum[layer] = -1;
01141 
01142   /* loop over all groups */
01143   for (s = group_string, group = 0;
01144        s && *s && group < *LayerN;
01145        group++)
01146     {
01147       while (*s && isspace ((int) *s))
01148         s++;
01149 
01150       /* loop over all group members */
01151       for (member = 0; *s; s++)
01152         {
01153           /* ignore white spaces and get layernumber */
01154           while (*s && isspace ((int) *s))
01155             s++;
01156           switch (*s)
01157             {
01158             case 'c':
01159             case 'C':
01160             case 't':
01161             case 'T':
01162               layer = *LayerN + TOP_SILK_LAYER;
01163               c_set = true;
01164               break;
01165 
01166             case 's':
01167             case 'S':
01168             case 'b':
01169             case 'B':
01170               layer = *LayerN + BOTTOM_SILK_LAYER;
01171               s_set = true;
01172               break;
01173 
01174             default:
01175               layer = atoi (s) - 1;
01176               break;
01177             }
01178           if (layer > *LayerN + MAX (BOTTOM_SILK_LAYER, TOP_SILK_LAYER) ||
01179               member >= *LayerN + 1)
01180             goto error;
01181           groupnum[layer] = group;
01182           LayerGroup->Entries[group][member++] = layer;
01183           while (*++s && isdigit ((int) *s));
01184 
01185           /* ignore white spaces and check for separator */
01186           while (*s && isspace ((int) *s))
01187             s++;
01188           if (!*s || *s == ':')
01189             break;
01190         }
01191       LayerGroup->Number[group] = member;
01192       if (*s == ':')
01193         s++;
01194     }
01195 
01196   /* If no explicit solder or component layer group was found in the layer
01197    * group string, make group 0 the bottom side, and group 1 the top side.
01198    * This is done by assigning the relevant silkscreen layers to those groups.
01199    */
01200   if (!s_set)
01201     LayerGroup->Entries[0][LayerGroup->Number[0]++] = *LayerN + BOTTOM_SILK_LAYER;
01202   if (!c_set)
01203     LayerGroup->Entries[1][LayerGroup->Number[1]++] = *LayerN + TOP_SILK_LAYER;
01204 
01205   /* Assign a unique layer group to each layer that was not explicitly
01206    * assigned a particular group by its presence in the layer group string.
01207    */
01208   for (layer = 0; layer < *LayerN && group < *LayerN; layer++)
01209     if (groupnum[layer] == -1)
01210       {
01211         LayerGroup->Entries[group][0] = layer;
01212         LayerGroup->Number[group] = 1;
01213         group++;
01214       }
01215   return (0);
01216 
01217   /* reset structure on error */
01218 error:
01219   memset (LayerGroup, 0, sizeof (LayerGroupType));
01220   return (1);
01221 }
01222 
01226 void
01227 AssignDefaultLayerTypes()
01228 {
01229   int num_found;
01230   Cardinal outline_layer = -1;
01231 
01232   /*
01233    * There can be only one outline layer. During parsing guess_layertype()
01234    * applied well known cases already, but as this function operates on a
01235    * single layer only, it might end up with more than one hit for the whole
01236    * file. Especially after loading an older layout without saved flags.
01237    */
01238   num_found = 0;
01239   LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
01240     outline_layer = n;
01241     num_found++;
01242   END_LOOP;
01243 
01244   if (num_found != 1)
01245     /* No or duplicate outline! Try to find a layer which is named exactly
01246        "outline". */
01247     LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
01248       if ( ! strcasecmp (layer->Name, "outline"))
01249         {
01250           outline_layer = n;
01251           num_found = 1;
01252           break;
01253         }
01254     END_LOOP;
01255 
01256   if (num_found != 1)
01257     /* Next, try to find a layer which is named exactly "route". */
01258     LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
01259       if ( ! strcasecmp (layer->Name, "route"))
01260         {
01261           outline_layer = n;
01262           num_found = 1;
01263           break;
01264         }
01265     END_LOOP;
01266 
01267   if (num_found != 1)
01268     /* As last resort, take the first layer claiming to be outline. */
01269     LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
01270       outline_layer = n;
01271       num_found = 1;
01272       break;
01273     END_LOOP;
01274 
01275   /* Make sure our found outline layer is the only one. */
01276   LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
01277     if (n == outline_layer)
01278       layer->Type = LT_OUTLINE;
01279     else
01280       layer->Type = LT_ROUTE;  /* best guess */
01281   END_LOOP;
01282 }
01283 
01284 extern void pcb_main_uninit(void);
01285 
01289 void
01290 QuitApplication (void)
01291 {
01292   /*
01293    * save data if necessary.  It not needed, then don't trigger EmergencySave
01294    * via our atexit() registering of EmergencySave().  We presumeably wanted to
01295    * exit here and thus it is not an emergency.
01296    */
01297   if (PCB->Changed && Settings.SaveInTMP)
01298     EmergencySave ();
01299   else
01300     DisableEmergencySave ();
01301 
01302   if (gui->do_exit == NULL)
01303     {
01304       pcb_main_uninit ();
01305       exit (0);
01306     }
01307   else
01308     gui->do_exit (gui);
01309 }
01310 
01318 char *
01319 EvaluateFilename (char *Template, char *Path, char *Filename, char *Parameter)
01320 {
01321   static DynamicStringType command;
01322   char *p;
01323 
01324   if (Settings.verbose)
01325     {
01326       printf ("EvaluateFilename:\n");
01327       printf ("\tTemplate: \033[33m%s\033[0m\n", Template);
01328       printf ("\tPath: \033[33m%s\033[0m\n", Path);
01329       printf ("\tFilename: \033[33m%s\033[0m\n", Filename);
01330       printf ("\tParameter: \033[33m%s\033[0m\n", Parameter);
01331     }
01332 
01333   DSClearString (&command);
01334 
01335   for (p = Template; p && *p; p++)
01336     {
01337       /* copy character or add string to command */
01338       if (*p == '%'
01339           && (*(p + 1) == 'f' || *(p + 1) == 'p' || *(p + 1) == 'a'))
01340         switch (*(++p))
01341           {
01342           case 'a':
01343             DSAddString (&command, Parameter);
01344             break;
01345           case 'f':
01346             DSAddString (&command, Filename);
01347             break;
01348           case 'p':
01349             DSAddString (&command, Path);
01350             break;
01351           }
01352       else
01353         DSAddCharacter (&command, *p);
01354     }
01355   DSAddCharacter (&command, '\0');
01356   if (Settings.verbose)
01357     printf ("EvaluateFilename: \033[32m%s\033[0m\n", command.Data);
01358 
01359   return strdup (command.Data);
01360 }
01361 
01368 char *
01369 ExpandFilename (char *Dirname, char *Filename)
01370 {
01371   static DynamicStringType answer;
01372   char *command;
01373   FILE *pipe;
01374   int c;
01375 
01376   /* allocate memory for commandline and build it */
01377   DSClearString (&answer);
01378   if (Dirname)
01379     {
01380       command = (char *)calloc (strlen (Filename) + strlen (Dirname) + 7,
01381                         sizeof (char));
01382       sprintf (command, "echo %s/%s", Dirname, Filename);
01383     }
01384   else
01385     {
01386       command = (char *)calloc (strlen (Filename) + 6, sizeof (char));
01387       sprintf (command, "echo %s", Filename);
01388     }
01389 
01390   /* execute it with shell */
01391   if ((pipe = popen (command, "r")) != NULL)
01392     {
01393       /* discard all but the first returned line */
01394       for (;;)
01395         {
01396           if ((c = fgetc (pipe)) == EOF || c == '\n' || c == '\r')
01397             break;
01398           else
01399             DSAddCharacter (&answer, c);
01400         }
01401 
01402       free (command);
01403       return (pclose (pipe) ? NULL : answer.Data);
01404     }
01405 
01406   /* couldn't be expanded by the shell */
01407   PopenErrorMessage (command);
01408   free (command);
01409   return (NULL);
01410 }
01411 
01412 
01416 int
01417 GetLayerNumber (DataType *Data, LayerType *Layer)
01418 {
01419   int i;
01420 
01421   for (i = 0; i < MAX_ALL_LAYER; i++)
01422     if (Layer == &Data->Layer[i])
01423       break;
01424   return (i);
01425 }
01426 
01430 static void
01431 PushOnTopOfLayerStack (int NewTop)
01432 {
01433   int i;
01434 
01435   /* ignore silk and other extra layers */
01436   if (NewTop < max_copper_layer)
01437     {
01438       /* first find position of passed one */
01439       for (i = 0; i < max_copper_layer; i++)
01440         if (LayerStack[i] == NewTop)
01441           break;
01442 
01443       /* bring this element to the top of the stack */
01444       for (; i; i--)
01445         LayerStack[i] = LayerStack[i - 1];
01446       LayerStack[0] = NewTop;
01447     }
01448 }
01449 
01450 
01456 int
01457 ChangeGroupVisibility (int Layer, bool On, bool ChangeStackOrder)
01458 {
01459   int group, i, changed = 1;    /* at least the current layer changes */
01460 
01461   /* Warning: these special case values must agree with what gui-top-window.c
01462      |  thinks the are.
01463    */
01464 
01465   if (Settings.verbose)
01466     printf ("ChangeGroupVisibility(Layer=%d, On=%d, ChangeStackOrder=%d)\n",
01467             Layer, On, ChangeStackOrder);
01468 
01469   /* decrement 'i' to keep stack in order of layergroup */
01470   group = GetLayerGroupNumberByNumber (Layer);
01471   for (i = PCB->LayerGroups.Number[group]; i;)
01472     {
01473       int layer = PCB->LayerGroups.Entries[group][--i];
01474 
01475       /* don't count the passed member of the group */
01476       if (layer != Layer && layer < max_copper_layer)
01477         {
01478           PCB->Data->Layer[layer].On = On;
01479 
01480           /* push layer on top of stack if switched on */
01481           if (On && ChangeStackOrder)
01482             PushOnTopOfLayerStack (layer);
01483           changed++;
01484         }
01485     }
01486 
01487   /* change at least the passed layer */
01488   PCB->Data->Layer[Layer].On = On;
01489   if (On && ChangeStackOrder)
01490     PushOnTopOfLayerStack (Layer);
01491 
01492   /* update control panel and exit */
01493   hid_action ("LayersChanged");
01494   return (changed);
01495 }
01496 
01501 void
01502 LayerStringToLayerStack (char *s)
01503 {
01504   static int listed_layers = 0;
01505   int l = strlen (s);
01506   char **args;
01507   int i, argn, lno;
01508   int prev_sep = 1;
01509 
01510   s = strdup (s);
01511   args = (char **) malloc (l * sizeof (char *));
01512   argn = 0;
01513 
01514   for (i=0; i<l; i++)
01515     {
01516       switch (s[i])
01517         {
01518         case ' ':
01519         case '\t':
01520         case ',':
01521         case ';':
01522         case ':':
01523           prev_sep = 1;
01524           s[i] = '\0';
01525           break;
01526         default:
01527           if (prev_sep)
01528             args[argn++] = s+i;
01529           prev_sep = 0;
01530           break;
01531         }
01532     }
01533 
01534   for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
01535     {
01536       if (i < max_copper_layer)
01537         LayerStack[i] = i;
01538       PCB->Data->Layer[i].On = false;
01539     }
01540   PCB->ElementOn = false;
01541   PCB->InvisibleObjectsOn = false;
01542   PCB->PinOn = false;
01543   PCB->ViaOn = false;
01544   PCB->RatOn = false;
01545   CLEAR_FLAG (SHOWMASKFLAG, PCB);
01546   Settings.ShowBottomSide = 0;
01547 
01548   for (i=argn-1; i>=0; i--)
01549     {
01550       if (strcasecmp (args[i], "rats") == 0)
01551         PCB->RatOn = true;
01552       else if (strcasecmp (args[i], "invisible") == 0)
01553         PCB->InvisibleObjectsOn = true;
01554       else if (strcasecmp (args[i], "pins") == 0)
01555         PCB->PinOn = true;
01556       else if (strcasecmp (args[i], "vias") == 0)
01557         PCB->ViaOn = true;
01558       else if (strcasecmp (args[i], "elements") == 0
01559                || strcasecmp (args[i], "silk") == 0)
01560         PCB->ElementOn = true;
01561       else if (strcasecmp (args[i], "mask") == 0)
01562         SET_FLAG (SHOWMASKFLAG, PCB);
01563       else if (strcasecmp (args[i], "solderside") == 0)
01564         Settings.ShowBottomSide = 1;
01565       else if (isdigit ((int) args[i][0]))
01566         {
01567           lno = atoi (args[i]);
01568           ChangeGroupVisibility (lno, true, true);
01569         }
01570       else
01571         {
01572           int found = 0;
01573           for (lno = 0; lno < max_copper_layer; lno++)
01574             if (strcasecmp (args[i], PCB->Data->Layer[lno].Name) == 0)
01575               {
01576                 ChangeGroupVisibility (lno, true, true);
01577                 found = 1;
01578                 break;
01579               }
01580           if (!found)
01581             {
01582               fprintf(stderr, _("Warning: layer \"%s\" not known\n"), args[i]);
01583               if (!listed_layers)
01584                 {
01585                   fprintf (stderr, _("Named layers in this board are:\n"));
01586                   listed_layers = 1;
01587                   for (lno=0; lno < max_copper_layer; lno ++)
01588                     fprintf(stderr, "\t%s\n", PCB->Data->Layer[lno].Name);
01589                   fprintf(stderr, _("Also: component, solder, rats, invisible, "
01590                         "pins, vias, elements or silk, mask, solderside.\n"));
01591                 }
01592             }
01593         }
01594     }
01595 }
01596 
01600 int
01601 GetLayerGroupNumberByPointer (LayerType *Layer)
01602 {
01603   return (GetLayerGroupNumberByNumber (GetLayerNumber (PCB->Data, Layer)));
01604 }
01605 
01609 int
01610 GetLayerGroupNumberByNumber (Cardinal Layer)
01611 {
01612   int group, entry;
01613 
01614   for (group = 0; group < max_group; group++)
01615     for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
01616       if (PCB->LayerGroups.Entries[group][entry] == Layer)
01617         return (group);
01618 
01619   /* since every layer belongs to a group it is safe to return
01620    * the value without boundary checking
01621    */
01622   return (group);
01623 }
01624 
01629 int
01630 GetLayerGroupNumberBySide (int side)
01631 {
01632   /* Find the relavant board side layer group by determining the
01633    * layer group associated with the relevant side's silk-screen
01634    */
01635   return GetLayerGroupNumberByNumber (
01636       side == TOP_SIDE ? top_silk_layer : bottom_silk_layer);
01637 }
01638 
01644 BoxType *
01645 GetObjectBoundingBox (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
01646 {
01647   switch (Type)
01648     {
01649     case LINE_TYPE:
01650     case ARC_TYPE:
01651     case TEXT_TYPE:
01652     case POLYGON_TYPE:
01653     case PAD_TYPE:
01654     case PIN_TYPE:
01655     case ELEMENTNAME_TYPE:
01656       return (BoxType *)Ptr2;
01657     case VIA_TYPE:
01658     case ELEMENT_TYPE:
01659       return (BoxType *)Ptr1;
01660     case POLYGONPOINT_TYPE:
01661     case LINEPOINT_TYPE:
01662       return (BoxType *)Ptr3;
01663     default:
01664       Message (_("Request for bounding box of unsupported type %d\n"), Type);
01665       return (BoxType *)Ptr2;
01666     }
01667 }
01668 
01672 void
01673 SetArcBoundingBox (ArcType *Arc)
01674 {
01675   double ca1, ca2, sa1, sa2;
01676   double minx, maxx, miny, maxy;
01677   Angle ang1, ang2;
01678   Coord width;
01679 
01680   /* first put angles into standard form:
01681    *  ang1 < ang2, both angles between 0 and 720 */
01682   Arc->Delta = CLAMP (Arc->Delta, -360, 360);
01683 
01684   if (Arc->Delta > 0)
01685     {
01686       ang1 = NormalizeAngle (Arc->StartAngle);
01687       ang2 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
01688     }
01689   else
01690     {
01691       ang1 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
01692       ang2 = NormalizeAngle (Arc->StartAngle);
01693     }
01694   if (ang1 > ang2)
01695     ang2 += 360;
01696   /* Make sure full circles aren't treated as zero-length arcs */
01697   if (Arc->Delta == 360 || Arc->Delta == -360)
01698     ang2 = ang1 + 360;
01699 
01700   /* calculate sines, cosines */
01701   sa1 = sin (M180 * ang1);
01702   ca1 = cos (M180 * ang1);
01703   sa2 = sin (M180 * ang2);
01704   ca2 = cos (M180 * ang2);
01705 
01706   minx = MIN (ca1, ca2);
01707   maxx = MAX (ca1, ca2);
01708   miny = MIN (sa1, sa2);
01709   maxy = MAX (sa1, sa2);
01710 
01711   /* Check for extreme angles */
01712   if ((ang1 <= 0   && ang2 >= 0)   || (ang1 <= 360 && ang2 >= 360)) maxx = 1;
01713   if ((ang1 <= 90  && ang2 >= 90)  || (ang1 <= 450 && ang2 >= 450)) maxy = 1;
01714   if ((ang1 <= 180 && ang2 >= 180) || (ang1 <= 540 && ang2 >= 540)) minx = -1;
01715   if ((ang1 <= 270 && ang2 >= 270) || (ang1 <= 630 && ang2 >= 630)) miny = -1;
01716 
01717   /* Finally, calcate bounds, converting sane geometry into pcb geometry */
01718   Arc->BoundingBox.X1 = Arc->X - Arc->Width * maxx;
01719   Arc->BoundingBox.X2 = Arc->X - Arc->Width * minx;
01720   Arc->BoundingBox.Y1 = Arc->Y + Arc->Height * miny;
01721   Arc->BoundingBox.Y2 = Arc->Y + Arc->Height * maxy;
01722 
01723   width = (Arc->Thickness + Arc->Clearance) / 2;
01724 
01725   /* Adjust for our discrete polygon approximation */
01726   width = (double)width * MAX (POLY_CIRC_RADIUS_ADJ, (1.0 + POLY_ARC_MAX_DEVIATION)) + 0.5;
01727 
01728   Arc->BoundingBox.X1 -= width;
01729   Arc->BoundingBox.X2 += width;
01730   Arc->BoundingBox.Y1 -= width;
01731   Arc->BoundingBox.Y2 += width;
01732   close_box(&Arc->BoundingBox);
01733 
01734   /* Update the arc end-points */
01735   Arc->Point1.X = Arc->X - (double)Arc->Width  * ca1;
01736   Arc->Point1.Y = Arc->Y + (double)Arc->Height * sa1;
01737   Arc->Point2.X = Arc->X - (double)Arc->Width  * ca2;
01738   Arc->Point2.Y = Arc->Y + (double)Arc->Height * sa2;
01739 }
01740 
01744 void
01745 ResetStackAndVisibility (void)
01746 {
01747   int top_group;
01748   Cardinal i;
01749 
01750   for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
01751     {
01752       if (i < max_copper_layer)
01753         LayerStack[i] = i;
01754       PCB->Data->Layer[i].On = true;
01755     }
01756   PCB->ElementOn = true;
01757   PCB->InvisibleObjectsOn = true;
01758   PCB->PinOn = true;
01759   PCB->ViaOn = true;
01760   PCB->RatOn = true;
01761 
01762   /* Bring the component group to the front and make it active.  */
01763   top_group = GetLayerGroupNumberBySide (TOP_SIDE);
01764   ChangeGroupVisibility (PCB->LayerGroups.Entries[top_group][0], 1, 1);
01765 }
01766 
01770 void
01771 SaveStackAndVisibility (void)
01772 {
01773   Cardinal i;
01774   static bool run = false;
01775 
01776   if (run == false)
01777     {
01778       SavedStack.cnt = 0;
01779       run = true;
01780     }
01781 
01782   if (SavedStack.cnt != 0)
01783     {
01784       fprintf (stderr,
01785                "SaveStackAndVisibility()  layerstack was already saved and not"
01786                "yet restored.  cnt = %d\n", SavedStack.cnt);
01787     }
01788 
01789   for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
01790     {
01791       if (i < max_copper_layer)
01792         SavedStack.LayerStack[i] = LayerStack[i];
01793       SavedStack.LayerOn[i] = PCB->Data->Layer[i].On;
01794     }
01795   SavedStack.ElementOn = PCB->ElementOn;
01796   SavedStack.InvisibleObjectsOn = PCB->InvisibleObjectsOn;
01797   SavedStack.PinOn = PCB->PinOn;
01798   SavedStack.ViaOn = PCB->ViaOn;
01799   SavedStack.RatOn = PCB->RatOn;
01800   SavedStack.cnt++;
01801 }
01802 
01806 void
01807 RestoreStackAndVisibility (void)
01808 {
01809   Cardinal i;
01810 
01811   if (SavedStack.cnt == 0)
01812     {
01813       fprintf (stderr, "RestoreStackAndVisibility()  layerstack has not"
01814                " been saved.  cnt = %d\n", SavedStack.cnt);
01815       return;
01816     }
01817   else if (SavedStack.cnt != 1)
01818     {
01819       fprintf (stderr, "RestoreStackAndVisibility()  layerstack save count is"
01820                " wrong.  cnt = %d\n", SavedStack.cnt);
01821     }
01822 
01823   for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
01824     {
01825       if (i < max_copper_layer)
01826         LayerStack[i] = SavedStack.LayerStack[i];
01827       PCB->Data->Layer[i].On = SavedStack.LayerOn[i];
01828     }
01829   PCB->ElementOn = SavedStack.ElementOn;
01830   PCB->InvisibleObjectsOn = SavedStack.InvisibleObjectsOn;
01831   PCB->PinOn = SavedStack.PinOn;
01832   PCB->ViaOn = SavedStack.ViaOn;
01833   PCB->RatOn = SavedStack.RatOn;
01834 
01835   SavedStack.cnt--;
01836 }
01837 
01844 char *
01845 GetWorkingDirectory (char *path)
01846 {
01847 #ifdef HAVE_GETCWD
01848   return getcwd (path, MAXPATHLEN);
01849 #else
01850   /* seems that some BSD releases lack of a prototype for getwd() */
01851   return getwd (path);
01852 #endif
01853 
01854 }
01855 
01861 void
01862 CreateQuotedString (DynamicStringType *DS, char *S)
01863 {
01864   DSClearString (DS);
01865   DSAddCharacter (DS, '"');
01866   while (*S)
01867     {
01868       if (*S == '"' || *S == '\\')
01869         DSAddCharacter (DS, '\\');
01870       DSAddCharacter (DS, *S++);
01871     }
01872   DSAddCharacter (DS, '"');
01873 }
01874 
01875 BoxType *
01876 GetArcEnds (ArcType *Arc)
01877 {
01878   static BoxType box;
01879   box.X1 = Arc->X - Arc->Width * cos (Arc->StartAngle * M180);
01880   box.Y1 = Arc->Y + Arc->Height * sin (Arc->StartAngle * M180);
01881   box.X2 = Arc->X - Arc->Width * cos ((Arc->StartAngle + Arc->Delta) * M180);
01882   box.Y2 = Arc->Y + Arc->Height * sin ((Arc->StartAngle + Arc->Delta) * M180);
01883   return &box;
01884 }
01885 
01886 
01890 void
01891 ChangeArcAngles (LayerType *Layer, ArcType *a,
01892                  Angle new_sa, Angle new_da)
01893 {
01894   if (new_da >= 360)
01895     {
01896       new_da = 360;
01897       new_sa = 0;
01898     }
01899   RestoreToPolygon (PCB->Data, ARC_TYPE, Layer, a);
01900   r_delete_entry (Layer->arc_tree, (BoxType *) a);
01901   AddObjectToChangeAnglesUndoList (ARC_TYPE, a, a, a);
01902   a->StartAngle = new_sa;
01903   a->Delta = new_da;
01904   SetArcBoundingBox (a);
01905   r_insert_entry (Layer->arc_tree, (BoxType *) a, 0);
01906   ClearFromPolygon (PCB->Data, ARC_TYPE, Layer, a);
01907 }
01908 
01909 static char *
01910 BumpName (char *Name)
01911 {
01912   int num;
01913   char c, *start;
01914   static char temp[256];
01915 
01916   start = Name;
01917   /* seek end of string */
01918   while (*Name != 0)
01919     Name++;
01920   /* back up to potential number */
01921   for (Name--; isdigit ((int) *Name); Name--);
01922   Name++;
01923   if (*Name)
01924     num = atoi (Name) + 1;
01925   else
01926     num = 1;
01927   c = *Name;
01928   *Name = 0;
01929   sprintf (temp, "%s%d", start, num);
01930   /* if this is not our string, put back the blown character */
01931   if (start != temp)
01932     *Name = c;
01933   return (temp);
01934 }
01935 
01941 char *
01942 UniqueElementName (DataType *Data, char *Name)
01943 {
01944   bool unique = true;
01945   /* null strings are ok */
01946   if (!Name || !*Name)
01947     return (Name);
01948 
01949   for (;;)
01950     {
01951       ELEMENT_LOOP (Data);
01952       {
01953         if (NAMEONPCB_NAME (element) &&
01954             NSTRCMP (NAMEONPCB_NAME (element), Name) == 0)
01955           {
01956             Name = BumpName (Name);
01957             unique = false;
01958             break;
01959           }
01960       }
01961       END_LOOP;
01962       if (unique)
01963         return (Name);
01964       unique = true;
01965     }
01966 }
01967 
01968 static void
01969 GetGridLockCoordinates (int type, void *ptr1,
01970                         void *ptr2, void *ptr3, Coord * x,
01971                         Coord * y)
01972 {
01973   switch (type)
01974     {
01975     case VIA_TYPE:
01976       *x = ((PinType *) ptr2)->X;
01977       *y = ((PinType *) ptr2)->Y;
01978       break;
01979     case LINE_TYPE:
01980       *x = ((LineType *) ptr2)->Point1.X;
01981       *y = ((LineType *) ptr2)->Point1.Y;
01982       break;
01983     case TEXT_TYPE:
01984     case ELEMENTNAME_TYPE:
01985       *x = ((TextType *) ptr2)->X;
01986       *y = ((TextType *) ptr2)->Y;
01987       break;
01988     case ELEMENT_TYPE:
01989       *x = ((ElementType *) ptr2)->MarkX;
01990       *y = ((ElementType *) ptr2)->MarkY;
01991       break;
01992     case POLYGON_TYPE:
01993       *x = ((PolygonType *) ptr2)->Points[0].X;
01994       *y = ((PolygonType *) ptr2)->Points[0].Y;
01995       break;
01996 
01997     case LINEPOINT_TYPE:
01998     case POLYGONPOINT_TYPE:
01999       *x = ((PointType *) ptr3)->X;
02000       *y = ((PointType *) ptr3)->Y;
02001       break;
02002     case ARC_TYPE:
02003       {
02004         BoxType *box;
02005 
02006         box = GetArcEnds ((ArcType *) ptr2);
02007         *x = box->X1;
02008         *y = box->Y1;
02009         break;
02010       }
02011     }
02012 }
02013 
02014 void
02015 AttachForCopy (Coord PlaceX, Coord PlaceY)
02016 {
02017   Coord mx = 0, my = 0;
02018 
02019   Crosshair.AttachedObject.RubberbandN = 0;
02020   if (! TEST_FLAG (SNAPPINFLAG, PCB))
02021     {
02022       /* dither the grab point so that the mark, center, etc
02023        * will end up on a grid coordinate
02024        */
02025       GetGridLockCoordinates (Crosshair.AttachedObject.Type,
02026                               Crosshair.AttachedObject.Ptr1,
02027                               Crosshair.AttachedObject.Ptr2,
02028                               Crosshair.AttachedObject.Ptr3, &mx, &my);
02029       mx = GridFit (mx, PCB->Grid, PCB->GridOffsetX) - mx;
02030       my = GridFit (my, PCB->Grid, PCB->GridOffsetY) - my;
02031     }
02032   Crosshair.AttachedObject.X = PlaceX - mx;
02033   Crosshair.AttachedObject.Y = PlaceY - my;
02034   if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
02035     SetLocalRef (PlaceX - mx, PlaceY - my, true);
02036   Crosshair.AttachedObject.State = STATE_SECOND;
02037 
02038   /* get boundingbox of object and set cursor range */
02039   crosshair_update_range();
02040 
02041   /* get all attached objects if necessary */
02042   if ((Settings.Mode != COPY_MODE) && TEST_FLAG (RUBBERBANDFLAG, PCB))
02043     LookupRubberbandLines (Crosshair.AttachedObject.Type,
02044                            Crosshair.AttachedObject.Ptr1,
02045                            Crosshair.AttachedObject.Ptr2,
02046                            Crosshair.AttachedObject.Ptr3);
02047   if (Settings.Mode != COPY_MODE &&
02048       (Crosshair.AttachedObject.Type == ELEMENT_TYPE ||
02049        Crosshair.AttachedObject.Type == VIA_TYPE ||
02050        Crosshair.AttachedObject.Type == LINE_TYPE ||
02051        Crosshair.AttachedObject.Type == LINEPOINT_TYPE))
02052     LookupRatLines (Crosshair.AttachedObject.Type,
02053                     Crosshair.AttachedObject.Ptr1,
02054                     Crosshair.AttachedObject.Ptr2,
02055                     Crosshair.AttachedObject.Ptr3);
02056 }
02057 
02061 int
02062 FileExists (const char *name)
02063 {
02064   FILE *f;
02065   f = fopen (name, "r");
02066   if (f)
02067     {
02068       fclose (f);
02069       return 1;
02070     }
02071   return 0;
02072 }
02073 
02074 char *
02075 Concat (const char *first, ...)
02076 {
02077   char *rv;
02078   int len;
02079   va_list a;
02080 
02081   len = strlen (first);
02082   rv = (char *) malloc (len + 1);
02083   strcpy (rv, first);
02084 
02085   va_start (a, first);
02086   while (1)
02087     {
02088       const char *s = va_arg (a, const char *);
02089       if (!s)
02090         break;
02091       len += strlen (s);
02092       rv = (char *) realloc (rv, len + 1);
02093       strcat (rv, s);
02094     }
02095   va_end (a);
02096   return rv;
02097 }
02098 
02099 int
02100 mem_any_set (unsigned char *ptr, int bytes)
02101 {
02102   while (bytes--)
02103     if (*ptr++)
02104       return 1;
02105   return 0;
02106 }
02107 
02111 FlagType
02112 MakeFlags (unsigned int flags)
02113 {
02114   FlagType rv;
02115   memset (&rv, 0, sizeof (rv));
02116   rv.f = flags;
02117   return rv;
02118 }
02119 
02124 FlagType
02125 OldFlags (unsigned int flags)
02126 {
02127   FlagType rv;
02128   int i, f;
02129   memset (&rv, 0, sizeof (rv));
02130   /* If we move flag bits around, this is where we map old bits to them.  */
02131   rv.f = flags & 0xffff;
02132   f = 0x10000;
02133   for (i = 0; i < 8; i++)
02134     {
02135       /* use the closest thing to the old thermal style */
02136       if (flags & f)
02137         rv.t[i / 2] |= (1 << (4 * (i % 2)));
02138       f <<= 1;
02139     }
02140   return rv;
02141 }
02142 
02143 FlagType
02144 AddFlags (FlagType flag, unsigned int flags)
02145 {
02146   flag.f |= flags;
02147   return flag;
02148 }
02149 
02150 FlagType
02151 MaskFlags (FlagType flag, unsigned int flags)
02152 {
02153   flag.f &= ~flags;
02154   return flag;
02155 }
02156 
02157 /* Layer Group Functions. */
02158 
02163 int
02164 MoveLayerToGroup (int layer, int group)
02165 {
02166   int prev, i, j;
02167 
02168   if (layer < 0 || layer > max_copper_layer + 1)
02169     return -1;
02170   prev = GetLayerGroupNumberByNumber (layer);
02171   if ((layer == bottom_silk_layer
02172         && group == GetLayerGroupNumberByNumber (top_silk_layer))
02173       || (layer == top_silk_layer
02174           && group == GetLayerGroupNumberByNumber (bottom_silk_layer))
02175       || (group < 0 || group >= max_group) || (prev == group))
02176     return prev;
02177 
02178   /* Remove layer from prev group */
02179   for (j = i = 0; i < PCB->LayerGroups.Number[prev]; i++)
02180     if (PCB->LayerGroups.Entries[prev][i] != layer)
02181       PCB->LayerGroups.Entries[prev][j++] = PCB->LayerGroups.Entries[prev][i];
02182   PCB->LayerGroups.Number[prev]--;
02183 
02184   /* Add layer to new group.  */
02185   i = PCB->LayerGroups.Number[group]++;
02186   PCB->LayerGroups.Entries[group][i] = layer;
02187 
02188   return group;
02189 }
02190 
02194 char *
02195 LayerGroupsToString (LayerGroupType *lg)
02196 {
02197 #if MAX_ALL_LAYER < 9999
02198   /* Allows for layer numbers 0..9999 */
02199   static char buf[(MAX_ALL_LAYER) * 5 + 1];
02200 #endif
02201   char *cp = buf;
02202   char sep = 0;
02203   int group, entry;
02204   for (group = 0; group < max_group; group++)
02205     if (PCB->LayerGroups.Number[group])
02206       {
02207         if (sep)
02208           *cp++ = ':';
02209         sep = 1;
02210         for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
02211           {
02212             int layer = PCB->LayerGroups.Entries[group][entry];
02213             if (layer == top_silk_layer)
02214               {
02215                 *cp++ = 'c';
02216               }
02217             else if (layer == bottom_silk_layer)
02218               {
02219                 *cp++ = 's';
02220               }
02221             else
02222               {
02223                 sprintf (cp, "%d", layer + 1);
02224                 while (*++cp)
02225                   ;
02226               }
02227             if (entry != PCB->LayerGroups.Number[group] - 1)
02228               *cp++ = ',';
02229           }
02230       }
02231   *cp++ = 0;
02232   return buf;
02233 }
02234 
02235 int
02236 GetMaxBottomLayer ()
02237 {
02238   int l = 0;
02239   int group, i;
02240 
02241   group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
02242   for (i = 0; i < PCB->LayerGroups.Number[group]; i++)
02243     {
02244       if (PCB->LayerGroups.Entries[group][i] < max_copper_layer)
02245         l = max (l, PCB->LayerGroups.Entries[group][i]);
02246     }
02247   return l;
02248 }
02249 
02250 
02258 void
02259 SanitizeBuriedVia (PinType *via)
02260 {
02261   /* we expect that via is burried/blind */
02262 
02263   if (via->BuriedFrom > via->BuriedTo)
02264     {
02265       int tmp = via->BuriedFrom;
02266       via->BuriedFrom = via->BuriedTo;
02267       via->BuriedTo = tmp;
02268     }
02269 
02270   /* check, if via was extended to full stack (after first layer removal)*/
02271   /* convert it in TH via in such case */
02272   if (via->BuriedTo == GetMaxBottomLayer ()
02273       && via->BuriedFrom == 0)
02274     via->BuriedTo = 0;
02275 }
02276 
02277 #if 0
02278 bool
02279 IsLayerMoveSafe (int old_index, int new_index)
02280 {
02281   VIA_LOOP (PCB->Data);
02282     {
02283       if (VIA_IS_BURIED (via))
02284         {
02285           /* moving from below to inside */
02286           if ((old_index < via->BuriedFrom)
02287               && (new_index < via->BuriedTo))
02288             return false;
02289 
02290           /* moving from above to inside */
02291           if ((old_index > via->BuriedTo)
02292               && (new_index > via->BuriedFrom))
02293             return false;
02294         }
02295     }
02296   END_LOOP;
02297 
02298   return true;
02299 }
02300 
02301 #endif
02302 
02308 void
02309 ChangeBuriedViasAfterLayerMove (int old_index, int new_index)
02310 {
02311   VIA_LOOP (PCB->Data);
02312     {
02313       if (VIA_IS_BURIED (via))
02314         {
02315           /* do nothing, if both layers are below the via */
02316           if ((old_index < via->BuriedFrom)
02317               && (new_index < via->BuriedFrom))
02318             continue;
02319 
02320           /* do nothing, if both layers are above the via */
02321           if ((old_index > via->BuriedTo)
02322               && (new_index > via->BuriedTo))
02323             continue;
02324 
02325           /* moving via top layer - via top follows layer */
02326           if (old_index == via->BuriedFrom)
02327             {
02328               AddObjectToSetViaLayersUndoList (via, via, via);
02329               via->BuriedFrom = new_index;
02330             }
02331 
02332           /* moving via bottom layer - via bottom follows layer */
02333           else if (old_index == via->BuriedTo)
02334             {
02335               AddObjectToSetViaLayersUndoList (via, via, via);
02336               via->BuriedTo = new_index;
02337             }
02338 
02339           /* moving layer covered by via inside the via (except top and bottom) - do nothing */
02340           else if (VIA_ON_LAYER (via, old_index)
02341               && VIA_ON_LAYER (via, new_index))
02342             continue;
02343 
02344           /* moving from inside to below - shrink via DANGEROUS op*/
02345           else if (VIA_ON_LAYER (via, old_index)
02346               && (new_index < via->BuriedFrom))
02347             {
02348               AddObjectToSetViaLayersUndoList (via, via, via);
02349               via->BuriedFrom++;
02350             }
02351 
02352           /* moving from inside to above - shrink via DANGEROUS op*/
02353           else if (VIA_ON_LAYER (via, old_index)
02354               && (new_index > via->BuriedTo))
02355             {
02356               AddObjectToSetViaLayersUndoList (via, via, via);
02357               via->BuriedTo--;
02358             }
02359 
02360           /* moving from above to below - shift via up */
02361           else if ((old_index > via->BuriedTo)
02362               && (new_index < via->BuriedFrom))
02363             {
02364               AddObjectToSetViaLayersUndoList (via, via, via);
02365               via->BuriedFrom++;
02366               via->BuriedTo++;
02367             }
02368 
02369           /* moving from below to above - shift via down */
02370           else if ((old_index < via->BuriedFrom)
02371               && (new_index >= via->BuriedTo))
02372             {
02373               AddObjectToSetViaLayersUndoList (via, via, via);
02374               via->BuriedFrom--;
02375               via->BuriedTo--;
02376             }
02377 
02378           /* moving from below to inside - extend via down DANGEROUS op*/
02379           else if ((old_index < via->BuriedFrom)
02380               && (new_index < via->BuriedTo))
02381             {
02382               AddObjectToSetViaLayersUndoList (via, via, via);
02383               via->BuriedFrom--;
02384             }
02385 
02386           /* moving from above to inside - extend via up DANGEROUS op*/
02387           else if ((old_index > via->BuriedTo)
02388               && (new_index > via->BuriedFrom))
02389             {
02390               AddObjectToSetViaLayersUndoList ( via, via, via);
02391               via->BuriedTo++;
02392             }
02393           else
02394             {
02395               Message (_("Layer move: failed via update: %d -> %d, via: %d:%d\n"), old_index, new_index, via->BuriedFrom, via->BuriedTo);
02396               continue;
02397             }
02398 
02399           SanitizeBuriedVia (via);
02400         }
02401     }
02402   END_LOOP;
02403   RemoveDegradedVias ();
02404 }
02405 
02410 void
02411 ChangeBuriedViasAfterLayerCreate (int index)
02412 {
02413   VIA_LOOP (PCB->Data);
02414     {
02415       if (VIA_IS_BURIED (via))
02416         {
02417           if (index <= via->BuriedFrom
02418               || index <= via->BuriedTo)
02419             AddObjectToSetViaLayersUndoList (via, via, via);
02420 
02421           if (index <= via->BuriedFrom)
02422             via->BuriedFrom++;
02423           if (index <= via->BuriedTo)
02424             via->BuriedTo++;
02425         }
02426     }
02427   END_LOOP;
02428 }
02429 
02434 void
02435 ChangeBuriedViasAfterLayerDelete (int index)
02436 {
02437   VIA_LOOP (PCB->Data);
02438     {
02439       if (VIA_IS_BURIED (via))
02440         {
02441           if (index < via->BuriedFrom
02442               || index <= via->BuriedTo)
02443             AddObjectToSetViaLayersUndoList (via, via, via);
02444 
02445           if (index < via->BuriedFrom)
02446             via->BuriedFrom--;
02447           if (index <= via->BuriedTo)
02448             via->BuriedTo--;
02449 
02450           SanitizeBuriedVia (via);
02451         }
02452     }
02453   END_LOOP;
02454   RemoveDegradedVias ();
02455 }
02456 
02461 bool
02462 ViaIsOnLayerGroup (PinType *via, int group)
02463 {
02464   Cardinal layer;
02465 
02466   if (!VIA_IS_BURIED (via))
02467     return true;
02468 
02469   for (layer = via->BuriedFrom; layer <= via->BuriedTo; layer++)
02470     {
02471        if (GetLayerGroupNumberByNumber (layer) == group)
02472          return true;
02473     }
02474 
02475   return false;
02476 }
02477 
02482 bool
02483 ViaIsOnAnyVisibleLayer (PinType *via)
02484 {
02485   Cardinal layer;
02486 
02487   if (!VIA_IS_BURIED (via))
02488     return true;
02489 
02490   for (layer = via->BuriedFrom; layer <= via->BuriedTo; layer ++)
02491     {
02492       if (PCB->Data->Layer[layer].On)
02493         return true;
02494     }
02495 
02496   return false;
02497 }
02498 
02499 
02500 char *
02501 pcb_author (void)
02502 {
02503 #ifdef HAVE_GETPWUID
02504   static struct passwd *pwentry;
02505   static char *fab_author = 0;
02506 
02507   if (!fab_author)
02508     {
02509       if (Settings.FabAuthor && Settings.FabAuthor[0])
02510         fab_author = Settings.FabAuthor;
02511       else
02512         {
02513           int len;
02514           char *comma, *gecos;
02515 
02516           /* ID the user. */
02517           pwentry = getpwuid (getuid ());
02518           gecos = pwentry->pw_gecos;
02519           comma = strchr (gecos, ',');
02520           if (comma)
02521             len = comma - gecos;
02522           else
02523             len = strlen (gecos);
02524           fab_author = (char *)malloc (len + 1);
02525           if (!fab_author)
02526             {
02527               perror ("pcb: out of memory.\n");
02528               exit (-1);
02529             }
02530           memcpy (fab_author, gecos, len);
02531           fab_author[len] = 0;
02532         }
02533     }
02534   return fab_author;
02535 #else
02536   return "Unknown";
02537 #endif
02538 }
02539 
02540 
02545 char *
02546 AttributeGetFromList (AttributeListType *list, char *name)
02547 {
02548   int i;
02549   for (i=0; i<list->Number; i++)
02550     if (strcmp (name, list->List[i].name) == 0)
02551       return list->List[i].value;
02552   return NULL;
02553 }
02554 
02563 int
02564 AttributePutToList (AttributeListType *list, const char *name, const char *value, int replace)
02565 {
02566   int i;
02567 
02568   /* If we're allowed to replace an existing attribute, see if we
02569      can.  */
02570   if (replace)
02571     {
02572       for (i=0; i<list->Number; i++)
02573         if (strcmp (name, list->List[i].name) == 0)
02574           {
02575             free (list->List[i].value);
02576             list->List[i].value = STRDUP (value);
02577             return 1;
02578           }
02579     }
02580 
02581   /* At this point, we're going to need to add a new attribute to the
02582      list.  See if there's room.  */
02583   if (list->Number >= list->Max)
02584     {
02585       list->Max += 10;
02586       list->List = (AttributeType *) realloc (list->List,
02587                                               list->Max * sizeof (AttributeType));
02588     }
02589 
02590   /* Now add the new attribute.  */
02591   i = list->Number;
02592   list->List[i].name = STRDUP (name);
02593   list->List[i].value = STRDUP (value);
02594   list->Number ++;
02595   return 0;
02596 }
02597 
02601 void
02602 AttributeRemoveFromList(AttributeListType *list, char *name)
02603 {
02604   int i, j;
02605   for (i=0; i<list->Number; i++)
02606     if (strcmp (name, list->List[i].name) == 0)
02607       {
02608         free (list->List[i].name);
02609         free (list->List[i].value);
02610         for (j=i; j<list->Number-1; j++)
02611           list->List[j] = list->List[j+1];
02612         list->Number --;
02613       }
02614 }
02615 
02622 const char *
02623 c_dtostr (double d)
02624 {
02625   static char buf[100];
02626   int i, f;
02627   char *bufp = buf;
02628 
02629   if (d < 0)
02630     {
02631       *bufp++ = '-';
02632       d = -d;
02633     }
02634   d += 0.0000005;               /* rounding */
02635   i = floor (d);
02636   d -= i;
02637   sprintf (bufp, "%d", i);
02638   bufp += strlen (bufp);
02639   *bufp++ = '.';
02640 
02641   f = floor (d * 1000000.0);
02642   sprintf (bufp, "%06d", f);
02643   return buf;
02644 }
02645 
02646 void
02647 r_delete_element (DataType * data, ElementType * element)
02648 {
02649   r_delete_entry (data->element_tree, (BoxType *) element);
02650   PIN_LOOP (element);
02651   {
02652     r_delete_entry (data->pin_tree, (BoxType *) pin);
02653   }
02654   END_LOOP;
02655   PAD_LOOP (element);
02656   {
02657     r_delete_entry (data->pad_tree, (BoxType *) pad);
02658   }
02659   END_LOOP;
02660   ELEMENTTEXT_LOOP (element);
02661   {
02662     r_delete_entry (data->name_tree[n], (BoxType *) text);
02663   }
02664   END_LOOP;
02665 }
02666 
02667 
02674 char *
02675 GetInfoString (void)
02676 {
02677   HID **hids;
02678   int i;
02679   static DynamicStringType info;
02680   static int first_time = 1;
02681 
02682 #define TAB "    "
02683 
02684   if (first_time)
02685     {
02686       first_time = 0;
02687       DSAddString (&info,
02688           _("This is PCB, an interactive\n"
02689             "printed circuit board editor\n"
02690             "version "));
02691       DSAddString (&info,
02692             VERSION "\n\n"
02693             "Compiled on " __DATE__ " at " __TIME__ "\n\n"
02694             "by harry eaton\n\n"
02695             "Copyright (C) Thomas Nau 1994, 1995, 1996, 1997\n"
02696             "Copyright (C) harry eaton 1998-2007\n"
02697             "Copyright (C) C. Scott Ananian 2001\n"
02698             "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n"
02699             "Copyright (C) Dan McMahill 2003, 2004, 2005, 2006, 2007, 2008\n\n");
02700       DSAddString (&info,
02701           _("It is licensed under the terms of the GNU\n"
02702             "General Public License version 2\n"
02703             "See the LICENSE file for more information\n\n"
02704             "For more information see:\n"));
02705       DSAddString (&info, _("PCB homepage: "));
02706       DSAddString (&info, "http://pcb.geda-project.org\n");
02707       DSAddString (&info, _("gEDA homepage: "));
02708       DSAddString (&info, "http://www.geda-project.org\n");
02709       DSAddString (&info, _("gEDA Wiki: "));
02710       DSAddString (&info, "http://wiki.geda-project.org\n");
02711 
02712       DSAddString (&info, _("\n----- Compile Time Options -----\n"));
02713       hids = hid_enumerate ();
02714       DSAddString (&info, _("GUI:\n"));
02715       for (i = 0; hids[i]; i++)
02716         {
02717           if (hids[i]->gui)
02718             {
02719               DSAddString (&info, TAB);
02720               DSAddString (&info, hids[i]->name);
02721               DSAddString (&info, " : ");
02722               DSAddString (&info, hids[i]->description);
02723               DSAddString (&info, "\n");
02724             }
02725         }
02726 
02727       DSAddString (&info, _("Exporters:\n"));
02728       for (i = 0; hids[i]; i++)
02729         {
02730           if (hids[i]->exporter)
02731             {
02732               DSAddString (&info, TAB);
02733               DSAddString (&info, hids[i]->name);
02734               DSAddString (&info, " : ");
02735               DSAddString (&info, hids[i]->description);
02736               DSAddString (&info, "\n");
02737             }
02738         }
02739 
02740       DSAddString (&info, _("Printers:\n"));
02741       for (i = 0; hids[i]; i++)
02742         {
02743           if (hids[i]->printer)
02744             {
02745               DSAddString (&info, TAB);
02746               DSAddString (&info, hids[i]->name);
02747               DSAddString (&info, " : ");
02748               DSAddString (&info, hids[i]->description);
02749               DSAddString (&info, "\n");
02750             }
02751         }
02752     }
02753 #undef TAB
02754 
02755   return info.Data;
02756 }
02757 
02758 #ifdef MKDIR_IS_PCBMKDIR
02759 #error "Don't know how to create a directory on this system."
02760 #endif
02761 
02766 int
02767 pcb_mkdir (const char *path, int mode)
02768 {
02769   return MKDIR (path, mode);
02770 }
02771 
02783 int 
02784 ElementOrientation (ElementType *e)
02785 {
02786   Coord pin1x, pin1y, pin2x, pin2y, dx, dy;
02787   bool found_pin1 = 0;
02788   bool found_pin2 = 0;
02789 
02790   /* in case we don't find pin 1 or 2, make sure we have initialized these variables */
02791   pin1x = 0;
02792   pin1y = 0;
02793   pin2x = 0;
02794   pin2y = 0;
02795 
02796   PIN_LOOP (e);
02797   {
02798     if (NSTRCMP (pin->Number, "1") == 0)
02799       {
02800         pin1x = pin->X;
02801         pin1y = pin->Y;
02802         found_pin1 = 1;
02803       }
02804     else if (NSTRCMP (pin->Number, "2") == 0)
02805       {
02806         pin2x = pin->X;
02807         pin2y = pin->Y;
02808         found_pin2 = 1;
02809       }
02810   }
02811   END_LOOP;
02812 
02813   PAD_LOOP (e);
02814   {
02815     if (NSTRCMP (pad->Number, "1") == 0)
02816       {
02817         pin1x = (pad->Point1.X + pad->Point2.X) / 2;
02818         pin1y = (pad->Point1.Y + pad->Point2.Y) / 2;
02819         found_pin1 = 1;
02820       }
02821     else if (NSTRCMP (pad->Number, "2") == 0)
02822       {
02823         pin2x = (pad->Point1.X + pad->Point2.X) / 2;
02824         pin2y = (pad->Point1.Y + pad->Point2.Y) / 2;
02825         found_pin2 = 1;
02826       }
02827   }
02828   END_LOOP;
02829 
02830   if (found_pin1 && found_pin2)
02831     {
02832       dx = pin2x - pin1x;
02833       dy = pin2y - pin1y;
02834     }
02835   else if (found_pin1 && (pin1x || pin1y))
02836     {
02837       dx = pin1x;
02838       dy = pin1y;
02839     }
02840   else if (found_pin2 && (pin2x || pin2y))
02841     {
02842       dx = pin2x;
02843       dy = pin2y;
02844     }
02845   else
02846     return 0;
02847 
02848   if (abs(dx) > abs(dy))
02849     return dx > 0 ? 0 : 2;
02850   return dy > 0 ? 3 : 1;
02851 }
02852 
02853 int
02854 ActionListRotations(int argc, char **argv, Coord x, Coord y)
02855 {
02856   ELEMENT_LOOP (PCB->Data);
02857   {
02858     printf("%d %s\n", ElementOrientation(element), NAMEONPCB_NAME(element));
02859   }
02860   END_LOOP;
02861 
02862   return 0;
02863 }
02864 
02865 HID_Action misc_action_list[] = {
02866   {"ListRotations", 0, ActionListRotations,
02867    0,0},
02868 };
02869 
02870 REGISTER_ACTIONS (misc_action_list)