1 /*!
2  * \file src/misc.c
3  *
4  * \brief Misc functions used by several modules.
5  *
6  * <hr>
7  *
8  * <h1><b>Copyright.</b></h1>\n
9  *
10  * PCB, interactive printed circuit board design
11  *
12  * Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License along
25  * with this program; if not, write to the Free Software Foundation, Inc.,
26  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27  *
28  * Contact addresses for paper mail and Email:
29  *
30  * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
31  *
32  * Thomas.Nau@rz.uni-ulm.de
33  */
34 
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38 
39 #include <stdlib.h>
40 #include <stdarg.h>
41 #ifdef HAVE_STRING_H
42 #include <string.h>
43 #endif
44 #include <memory.h>
45 #include <ctype.h>
46 #include <signal.h>
47 #include <sys/param.h>
48 #include <sys/stat.h>
49 #include <sys/types.h>
50 #include <math.h>
51 #ifdef HAVE_UNISTD_H
52 #include <unistd.h>
53 #endif
54 #ifdef HAVE_PWD_H
55 #include <pwd.h>
56 #endif
57 
58 #include "global.h"
59 
60 #include "box.h"
61 #include "crosshair.h"
62 #include "create.h"
63 #include "data.h"
64 #include "draw.h"
65 #include "file.h"
66 #include "error.h"
67 #include "mymem.h"
68 #include "misc.h"
69 #include "move.h"
70 #include "pcb-printf.h"
71 #include "polygon.h"
72 #include "remove.h"
73 #include "rtree.h"
74 #include "rotate.h"
75 #include "rubberband.h"
76 #include "search.h"
77 #include "set.h"
78 #include "undo.h"
79 #include "action.h"
80 
81 #ifdef HAVE_LIBDMALLOC
82 #include <dmalloc.h>
83 #endif
84 
85 /*	forward declarations	*/
86 static char *BumpName (char *);
87 static void GetGridLockCoordinates (int, void *, void *, void *,
88                                     Coord *, Coord *);
89 
90 
91 /* Local variables */
92 
93 /*!
94  * \brief Used by SaveStackAndVisibility() and
95  * RestoreStackAndVisibility().
96  */
97 static struct
98 {
99   bool ElementOn, InvisibleObjectsOn, PinOn, ViaOn, RatOn;
100   int LayerStack[MAX_ALL_LAYER];
101   bool LayerOn[MAX_ALL_LAYER];
102   int cnt;
103 } SavedStack;
104 
105 /*!
106  * \brief Distance() should be used so that there is only one place to
107  * deal with overflow/precision errors.
108  */
109 double
Distance(double x1,double y1,double x2,double y2)110 Distance (double x1, double y1, double x2, double y2)
111 {
112   return hypot(x2 - x1, y2 - y1);
113 }
114 
115 /*!
116  * \brief Bring an angle into [0, 360] range.
117  */
118 Angle
NormalizeAngle(Angle a)119 NormalizeAngle (Angle a)
120 {
121   while (a < 0)
122     a += 360.0;
123   while (a >= 360.0)
124     a -= 360.0;
125   return a;
126 }
127 
128 /*!
129  * \brief GetValue() returns a numeric value passed from the string and
130  * sets the bool variable absolute to false if it leads with a +/-
131  * character.
132  */
133 double
GetValue(const char * val,const char * units,bool * absolute)134 GetValue (const char *val, const char *units, bool * absolute)
135 {
136   return GetValueEx(val, units, absolute, NULL, "cmil");
137 }
138 
139 double
GetValueEx(const char * val,const char * units,bool * absolute,UnitList extra_units,const char * default_unit)140 GetValueEx (const char *val, const char *units, bool * absolute, UnitList extra_units, const char *default_unit)
141 {
142   double value;
143   int n = -1;
144   bool scaled = 0;
145   bool dummy;
146 
147   /* Allow NULL to be passed for absolute */
148   if(absolute == NULL)
149     absolute = &dummy;
150 
151   /* if the first character is a sign we have to add the
152    * value to the current one
153    */
154   if (*val == '=')
155     {
156       *absolute = true;
157       if (sscanf (val+1, "%lf%n", &value, &n) < 1)
158         return 0;
159       n++;
160     }
161   else
162     {
163       if (isdigit ((int) *val))
164         *absolute = true;
165       else
166         *absolute = false;
167       if (sscanf (val, "%lf%n", &value, &n) < 1)
168         return 0;
169     }
170   if (!units && n > 0)
171     units = val + n;
172 
173   while (units && *units == ' ')
174     units ++;
175 
176   if (units && *units)
177     {
178       int i;
179       const Unit *unit = get_unit_struct (units);
180       if (unit != NULL)
181         {
182           value  = unit_to_coord (unit, value);
183           scaled = 1;
184         }
185       if (extra_units)
186         {
187           for (i = 0; *extra_units[i].suffix; ++i)
188             {
189               if (strncmp (units, extra_units[i].suffix, strlen(extra_units[i].suffix)) == 0)
190                 {
191                   value *= extra_units[i].scale;
192                   if (extra_units[i].flags & UNIT_PERCENT)
193                     value /= 100.0;
194                   scaled = 1;
195                 }
196             }
197         }
198     }
199   /* Apply default unit */
200   if (!scaled && default_unit && *default_unit)
201     {
202       int i;
203       const Unit *unit = get_unit_struct (default_unit);
204       if (extra_units)
205         for (i = 0; *extra_units[i].suffix; ++i)
206           if (strcmp (extra_units[i].suffix, default_unit) == 0)
207             {
208               value *= extra_units[i].scale;
209               if (extra_units[i].flags & UNIT_PERCENT)
210                 value /= 100.0;
211               scaled = 1;
212             }
213       if (!scaled && unit != NULL)
214         value = unit_to_coord (unit, value);
215     }
216 
217   return value;
218 }
219 
220 /*!
221  * \brief Extract a unit-less value from a string.
222  *
223  * \param val       String containing the value to be read.
224  *
225  * \param absolute  Returns whether the returned value is an absolute one.
226  *
227  * \return The value read, with sign.
228  *
229  * This is the same as GetValue() and GetValueEx(), but totally ignoring units.
230  * Typical application is a list selector, like the type of thermal to apply
231  * to a pin.
232  */
GetUnitlessValue(const char * val,bool * absolute)233 double GetUnitlessValue (const char *val, bool *absolute) {
234   double value;
235 
236   if (*val == '=')
237     {
238       *absolute = true;
239       val++;
240     }
241   else
242     {
243       if (isdigit ((int) *val))
244         *absolute = true;
245       else
246         *absolute = false;
247     }
248 
249   if (sscanf (val, "%lf", &value) < 1)
250     return 0.;
251 
252   return value;
253 }
254 
255 /*!
256  * \brief Sets the bounding box of a point (which is silly).
257  */
258 void
SetPointBoundingBox(PointType * Pnt)259 SetPointBoundingBox (PointType *Pnt)
260 {
261   Pnt->X2 = Pnt->X + 1;
262   Pnt->Y2 = Pnt->Y + 1;
263 }
264 
265 /*!
266  * \brief Sets the bounding box of a pin or via.
267  */
268 void
SetPinBoundingBox(PinType * Pin)269 SetPinBoundingBox (PinType *Pin)
270 {
271   Coord width;
272 
273   /* the bounding box covers the extent of influence
274    * so it must include the clearance values too
275    */
276   width = MAX (Pin->Clearance + PIN_SIZE (Pin), Pin->Mask) / 2;
277 
278   /* Adjust for our discrete polygon approximation */
279   width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
280 
281   Pin->BoundingBox.X1 = Pin->X - width;
282   Pin->BoundingBox.Y1 = Pin->Y - width;
283   Pin->BoundingBox.X2 = Pin->X + width;
284   Pin->BoundingBox.Y2 = Pin->Y + width;
285   close_box(&Pin->BoundingBox);
286 }
287 
288 /*!
289  * \brief Sets the bounding box of a pad.
290  */
291 void
SetPadBoundingBox(PadType * Pad)292 SetPadBoundingBox (PadType *Pad)
293 {
294   Coord width;
295   Coord deltax;
296   Coord deltay;
297 
298   /* the bounding box covers the extent of influence
299    * so it must include the clearance values too
300    */
301   width = (Pad->Thickness + Pad->Clearance + 1) / 2;
302   width = MAX (width, (Pad->Mask + 1) / 2);
303   deltax = Pad->Point2.X - Pad->Point1.X;
304   deltay = Pad->Point2.Y - Pad->Point1.Y;
305 
306   if (TEST_FLAG (SQUAREFLAG, Pad) && deltax != 0 && deltay != 0)
307     {
308       /* slanted square pad */
309       double theta;
310       Coord btx, bty;
311 
312       /* T is a vector half a thickness long, in the direction of
313           one of the corners.  */
314       theta = atan2 (deltay, deltax);
315       btx = width * cos (theta + M_PI/4) * sqrt(2.0);
316       bty = width * sin (theta + M_PI/4) * sqrt(2.0);
317 
318 
319       Pad->BoundingBox.X1 = MIN (MIN (Pad->Point1.X - btx, Pad->Point1.X - bty),
320                                  MIN (Pad->Point2.X + btx, Pad->Point2.X + bty));
321       Pad->BoundingBox.X2 = MAX (MAX (Pad->Point1.X - btx, Pad->Point1.X - bty),
322                                  MAX (Pad->Point2.X + btx, Pad->Point2.X + bty));
323       Pad->BoundingBox.Y1 = MIN (MIN (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
324                                  MIN (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
325       Pad->BoundingBox.Y2 = MAX (MAX (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
326                                  MAX (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
327     }
328   else
329     {
330       /* Adjust for our discrete polygon approximation */
331       width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
332 
333       Pad->BoundingBox.X1 = MIN (Pad->Point1.X, Pad->Point2.X) - width;
334       Pad->BoundingBox.X2 = MAX (Pad->Point1.X, Pad->Point2.X) + width;
335       Pad->BoundingBox.Y1 = MIN (Pad->Point1.Y, Pad->Point2.Y) - width;
336       Pad->BoundingBox.Y2 = MAX (Pad->Point1.Y, Pad->Point2.Y) + width;
337     }
338   close_box(&Pad->BoundingBox);
339 }
340 
341 /*!
342  * \brief Sets the bounding box of a line.
343  */
344 void
SetLineBoundingBox(LineType * Line)345 SetLineBoundingBox (LineType *Line)
346 {
347   Coord width = (Line->Thickness + Line->Clearance + 1) / 2;
348 
349   /* Adjust for our discrete polygon approximation */
350   width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
351 
352   Line->BoundingBox.X1 = MIN (Line->Point1.X, Line->Point2.X) - width;
353   Line->BoundingBox.X2 = MAX (Line->Point1.X, Line->Point2.X) + width;
354   Line->BoundingBox.Y1 = MIN (Line->Point1.Y, Line->Point2.Y) - width;
355   Line->BoundingBox.Y2 = MAX (Line->Point1.Y, Line->Point2.Y) + width;
356   close_box(&Line->BoundingBox);
357   SetPointBoundingBox (&Line->Point1);
358   SetPointBoundingBox (&Line->Point2);
359 }
360 
361 /*!
362  * \brief Sets the bounding box of a polygons.
363  */
364 void
SetPolygonBoundingBox(PolygonType * Polygon)365 SetPolygonBoundingBox (PolygonType *Polygon)
366 {
367   Polygon->BoundingBox.X1 = Polygon->BoundingBox.Y1 = MAX_COORD;
368   Polygon->BoundingBox.X2 = Polygon->BoundingBox.Y2 = 0;
369   POLYGONPOINT_LOOP (Polygon);
370   {
371     MAKEMIN (Polygon->BoundingBox.X1, point->X);
372     MAKEMIN (Polygon->BoundingBox.Y1, point->Y);
373     MAKEMAX (Polygon->BoundingBox.X2, point->X);
374     MAKEMAX (Polygon->BoundingBox.Y2, point->Y);
375   }
376   /* boxes don't include the lower right corner */
377   close_box(&Polygon->BoundingBox);
378   END_LOOP;
379 }
380 
381 /*!
382  * \brief Sets the bounding box of an element.
383  */
384 void
SetElementBoundingBox(DataType * Data,ElementType * Element,FontType * Font)385 SetElementBoundingBox (DataType *Data, ElementType *Element,
386                        FontType *Font)
387 {
388   BoxType *box, *vbox;
389 
390   if (Data && Data->element_tree)
391     r_delete_entry (Data->element_tree, (BoxType *) Element);
392   /* first update the text objects */
393   ELEMENTTEXT_LOOP (Element);
394   {
395     if (Data && Data->name_tree[n])
396       r_delete_entry (Data->name_tree[n], (BoxType *) text);
397     SetTextBoundingBox (Font, text);
398     if (Data && !Data->name_tree[n])
399       Data->name_tree[n] = r_create_tree (NULL, 0, 0);
400     if (Data)
401       r_insert_entry (Data->name_tree[n], (BoxType *) text, 0);
402   }
403   END_LOOP;
404 
405   /* do not include the elementnames bounding box which
406    * is handled separately
407    */
408   box = &Element->BoundingBox;
409   vbox = &Element->VBox;
410   box->X1 = box->Y1 = MAX_COORD;
411   box->X2 = box->Y2 = 0;
412   ELEMENTLINE_LOOP (Element);
413   {
414     SetLineBoundingBox (line);
415     MAKEMIN (box->X1, line->Point1.X - (line->Thickness + 1) / 2);
416     MAKEMIN (box->Y1, line->Point1.Y - (line->Thickness + 1) / 2);
417     MAKEMIN (box->X1, line->Point2.X - (line->Thickness + 1) / 2);
418     MAKEMIN (box->Y1, line->Point2.Y - (line->Thickness + 1) / 2);
419     MAKEMAX (box->X2, line->Point1.X + (line->Thickness + 1) / 2);
420     MAKEMAX (box->Y2, line->Point1.Y + (line->Thickness + 1) / 2);
421     MAKEMAX (box->X2, line->Point2.X + (line->Thickness + 1) / 2);
422     MAKEMAX (box->Y2, line->Point2.Y + (line->Thickness + 1) / 2);
423   }
424   END_LOOP;
425   ARC_LOOP (Element);
426   {
427     SetArcBoundingBox (arc);
428     MAKEMIN (box->X1, arc->BoundingBox.X1);
429     MAKEMIN (box->Y1, arc->BoundingBox.Y1);
430     MAKEMAX (box->X2, arc->BoundingBox.X2);
431     MAKEMAX (box->Y2, arc->BoundingBox.Y2);
432   }
433   END_LOOP;
434   *vbox = *box;
435   PIN_LOOP (Element);
436   {
437     if (Data && Data->pin_tree)
438       r_delete_entry (Data->pin_tree, (BoxType *) pin);
439     SetPinBoundingBox (pin);
440     if (Data)
441       {
442         if (!Data->pin_tree)
443           Data->pin_tree = r_create_tree (NULL, 0, 0);
444         r_insert_entry (Data->pin_tree, (BoxType *) pin, 0);
445       }
446     MAKEMIN (box->X1, pin->BoundingBox.X1);
447     MAKEMIN (box->Y1, pin->BoundingBox.Y1);
448     MAKEMAX (box->X2, pin->BoundingBox.X2);
449     MAKEMAX (box->Y2, pin->BoundingBox.Y2);
450     MAKEMIN (vbox->X1, pin->X - pin->Thickness / 2);
451     MAKEMIN (vbox->Y1, pin->Y - pin->Thickness / 2);
452     MAKEMAX (vbox->X2, pin->X + pin->Thickness / 2);
453     MAKEMAX (vbox->Y2, pin->Y + pin->Thickness / 2);
454   }
455   END_LOOP;
456   PAD_LOOP (Element);
457   {
458     if (Data && Data->pad_tree)
459       r_delete_entry (Data->pad_tree, (BoxType *) pad);
460     SetPadBoundingBox (pad);
461     if (Data)
462       {
463         if (!Data->pad_tree)
464           Data->pad_tree = r_create_tree (NULL, 0, 0);
465         r_insert_entry (Data->pad_tree, (BoxType *) pad, 0);
466       }
467     MAKEMIN (box->X1, pad->BoundingBox.X1);
468     MAKEMIN (box->Y1, pad->BoundingBox.Y1);
469     MAKEMAX (box->X2, pad->BoundingBox.X2);
470     MAKEMAX (box->Y2, pad->BoundingBox.Y2);
471     MAKEMIN (vbox->X1,
472              MIN (pad->Point1.X, pad->Point2.X) - pad->Thickness / 2);
473     MAKEMIN (vbox->Y1,
474              MIN (pad->Point1.Y, pad->Point2.Y) - pad->Thickness / 2);
475     MAKEMAX (vbox->X2,
476              MAX (pad->Point1.X, pad->Point2.X) + pad->Thickness / 2);
477     MAKEMAX (vbox->Y2,
478              MAX (pad->Point1.Y, pad->Point2.Y) + pad->Thickness / 2);
479   }
480   END_LOOP;
481   /* now we set the EDGE2FLAG of the pad if Point2
482    * is closer to the outside edge than Point1
483    */
484   PAD_LOOP (Element);
485   {
486     if (pad->Point1.Y == pad->Point2.Y)
487       {
488         /* horizontal pad */
489         if (box->X2 - pad->Point2.X < pad->Point1.X - box->X1)
490           SET_FLAG (EDGE2FLAG, pad);
491         else
492           CLEAR_FLAG (EDGE2FLAG, pad);
493       }
494     else
495       {
496         /* vertical pad */
497         if (box->Y2 - pad->Point2.Y < pad->Point1.Y - box->Y1)
498           SET_FLAG (EDGE2FLAG, pad);
499         else
500           CLEAR_FLAG (EDGE2FLAG, pad);
501       }
502   }
503   END_LOOP;
504 
505   /* mark pins with component orientation */
506   if ((box->X2 - box->X1) > (box->Y2 - box->Y1))
507     {
508       PIN_LOOP (Element);
509       {
510         SET_FLAG (EDGE2FLAG, pin);
511       }
512       END_LOOP;
513     }
514   else
515     {
516       PIN_LOOP (Element);
517       {
518         CLEAR_FLAG (EDGE2FLAG, pin);
519       }
520       END_LOOP;
521     }
522   close_box(box);
523   close_box(vbox);
524   if (Data && !Data->element_tree)
525     Data->element_tree = r_create_tree (NULL, 0, 0);
526   if (Data)
527     r_insert_entry (Data->element_tree, box, 0);
528 }
529 
530 /*!
531  * \brief Creates the bounding box of a text object.
532  */
533 void
SetTextBoundingBox(FontType * FontPtr,TextType * Text)534 SetTextBoundingBox (FontType *FontPtr, TextType *Text)
535 {
536   SymbolType *symbol = FontPtr->Symbol;
537   unsigned char *s = (unsigned char *) Text->TextString;
538   int i;
539   int space;
540 
541   Coord minx, miny, maxx, maxy, tx;
542   Coord min_final_radius;
543   Coord min_unscaled_radius;
544   bool first_time = true;
545 
546   minx = miny = maxx = maxy = tx = 0;
547 
548   /* Calculate the bounding box based on the larger of the thicknesses
549    * the text might clamped at on silk or copper layers.
550    */
551   min_final_radius = MAX (PCB->minWid, PCB->minSlk) / 2;
552 
553   /* Pre-adjust the line radius for the fact we are initially computing the
554    * bounds of the un-scaled text, and the thickness clamping applies to
555    * scaled text.
556    */
557   min_unscaled_radius = UNSCALE_TEXT (min_final_radius, Text->Scale);
558 
559   /* calculate size of the bounding box */
560   for (; s && *s; s++)
561     {
562       if (*s <= MAX_FONTPOSITION && symbol[*s].Valid)
563         {
564           LineType *line = symbol[*s].Line;
565           for (i = 0; i < symbol[*s].LineN; line++, i++)
566             {
567               /* Clamp the width of text lines at the minimum thickness.
568                * NB: Divide 4 in thickness calculation is comprised of a factor
569                *     of 1/2 to get a radius from the center-line, and a factor
570                *     of 1/2 because some stupid reason we render our glyphs
571                *     at half their defined stroke-width.
572                */
573                Coord unscaled_radius = MAX (min_unscaled_radius, line->Thickness / 4);
574 
575               if (first_time)
576                 {
577                   minx = maxx = line->Point1.X;
578                   miny = maxy = line->Point1.Y;
579                   first_time = false;
580                 }
581 
582               minx = MIN (minx, line->Point1.X - unscaled_radius + tx);
583               miny = MIN (miny, line->Point1.Y - unscaled_radius);
584               minx = MIN (minx, line->Point2.X - unscaled_radius + tx);
585               miny = MIN (miny, line->Point2.Y - unscaled_radius);
586               maxx = MAX (maxx, line->Point1.X + unscaled_radius + tx);
587               maxy = MAX (maxy, line->Point1.Y + unscaled_radius);
588               maxx = MAX (maxx, line->Point2.X + unscaled_radius + tx);
589               maxy = MAX (maxy, line->Point2.Y + unscaled_radius);
590             }
591           space = symbol[*s].Delta;
592         }
593       else
594         {
595           BoxType *ds = &FontPtr->DefaultSymbol;
596           Coord w = ds->X2 - ds->X1;
597 
598           minx = MIN (minx, ds->X1 + tx);
599           miny = MIN (miny, ds->Y1);
600           minx = MIN (minx, ds->X2 + tx);
601           miny = MIN (miny, ds->Y2);
602           maxx = MAX (maxx, ds->X1 + tx);
603           maxy = MAX (maxy, ds->Y1);
604           maxx = MAX (maxx, ds->X2 + tx);
605           maxy = MAX (maxy, ds->Y2);
606 
607           space = w / 5;
608         }
609       tx += symbol[*s].Width + space;
610     }
611 
612   /* scale values */
613   minx = SCALE_TEXT (minx, Text->Scale);
614   miny = SCALE_TEXT (miny, Text->Scale);
615   maxx = SCALE_TEXT (maxx, Text->Scale);
616   maxy = SCALE_TEXT (maxy, Text->Scale);
617 
618   /* set upper-left and lower-right corner;
619    * swap coordinates if necessary (origin is already in 'swapped')
620    * and rotate box
621    */
622 
623   if (TEST_FLAG (ONSOLDERFLAG, Text))
624     {
625       Text->BoundingBox.X1 = Text->X + minx;
626       Text->BoundingBox.Y1 = Text->Y - miny;
627       Text->BoundingBox.X2 = Text->X + maxx;
628       Text->BoundingBox.Y2 = Text->Y - maxy;
629       RotateBoxLowLevel (&Text->BoundingBox, Text->X, Text->Y,
630                          (4 - Text->Direction) & 0x03);
631     }
632   else
633     {
634       Text->BoundingBox.X1 = Text->X + minx;
635       Text->BoundingBox.Y1 = Text->Y + miny;
636       Text->BoundingBox.X2 = Text->X + maxx;
637       Text->BoundingBox.Y2 = Text->Y + maxy;
638       RotateBoxLowLevel (&Text->BoundingBox,
639                          Text->X, Text->Y, Text->Direction);
640     }
641 
642   /* the bounding box covers the extent of influence
643    * so it must include the clearance values too
644    */
645   Text->BoundingBox.X1 -= PCB->Bloat;
646   Text->BoundingBox.Y1 -= PCB->Bloat;
647   Text->BoundingBox.X2 += PCB->Bloat;
648   Text->BoundingBox.Y2 += PCB->Bloat;
649   close_box(&Text->BoundingBox);
650 }
651 
652 /*!
653  * \brief Returns true if data area is empty.
654  */
655 bool
IsDataEmpty(DataType * Data)656 IsDataEmpty (DataType *Data)
657 {
658   bool hasNoObjects;
659   Cardinal i;
660 
661   hasNoObjects = (Data->ViaN == 0);
662   hasNoObjects &= (Data->ElementN == 0);
663   for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
664     hasNoObjects = hasNoObjects &&
665       Data->Layer[i].LineN == 0 &&
666       Data->Layer[i].ArcN == 0 &&
667       Data->Layer[i].TextN == 0 && Data->Layer[i].PolygonN == 0;
668   return (hasNoObjects);
669 }
670 
671 int
FlagIsDataEmpty(int parm)672 FlagIsDataEmpty (int parm)
673 {
674   int i = IsDataEmpty (PCB->Data);
675   return parm ? !i : i;
676 }
677 
678 /* FLAG(DataEmpty,FlagIsDataEmpty,0) */
679 /* FLAG(DataNonEmpty,FlagIsDataEmpty,1) */
680 
681 bool
IsLayerEmpty(LayerType * layer)682 IsLayerEmpty (LayerType *layer)
683 {
684   return (layer->LineN == 0
685 	  && layer->TextN == 0
686 	  && layer->PolygonN == 0
687 	  && layer->ArcN == 0);
688 }
689 
690 bool
IsLayerNumEmpty(int num)691 IsLayerNumEmpty (int num)
692 {
693   return IsLayerEmpty (PCB->Data->Layer+num);
694 }
695 
696 bool
IsLayerGroupEmpty(int num)697 IsLayerGroupEmpty (int num)
698 {
699   int i;
700   for (i=0; i<PCB->LayerGroups.Number[num]; i++)
701     if (!IsLayerNumEmpty (PCB->LayerGroups.Entries[num][i]))
702       return false;
703   return true;
704 }
705 
706 bool
IsPasteEmpty(int side)707 IsPasteEmpty (int side)
708 {
709   bool paste_empty = true;
710   ALLPAD_LOOP (PCB->Data);
711   {
712     if (ON_SIDE (pad, side) && !TEST_FLAG (NOPASTEFLAG, pad) && pad->Mask > 0)
713       {
714         paste_empty = false;
715         break;
716       }
717   }
718   ENDALL_LOOP;
719   return paste_empty;
720 }
721 
722 
723 typedef struct
724 {
725   int nplated;
726   int nunplated;
727   Cardinal group_from;
728   Cardinal group_to;
729 } HoleCountStruct;
730 
731 static int
hole_counting_callback(const BoxType * b,void * cl)732 hole_counting_callback (const BoxType * b, void *cl)
733 {
734   PinType *pin = (PinType *) b;
735   HoleCountStruct *hcs = (HoleCountStruct *) cl;
736 
737   if (hcs->group_from != 0
738           || hcs->group_to != 0)
739     {
740       if (VIA_IS_BURIED (pin))
741         {
742           if (hcs->group_from == GetLayerGroupNumberByNumber (pin->BuriedFrom)
743               && hcs->group_to == GetLayerGroupNumberByNumber (pin->BuriedTo))
744 	    goto count;
745 	}
746       return 1;
747     }
748 
749 count:
750   if (TEST_FLAG (HOLEFLAG, pin))
751     hcs->nunplated++;
752   else
753     hcs->nplated++;
754   return 1;
755 }
756 
757 /*!
758  * \brief Counts the number of plated and unplated holes in the design
759  * within a given area of the board.
760  *
761  * To count for the whole board, pass NULL to the within_area.
762  */
763 void
CountHoles(int * plated,int * unplated,const BoxType * within_area)764 CountHoles (int *plated, int *unplated, const BoxType *within_area)
765 {
766   HoleCountStruct hcs = {0, 0, 0, 0};
767 
768   r_search (PCB->Data->pin_tree, within_area, NULL, hole_counting_callback, &hcs);
769   r_search (PCB->Data->via_tree, within_area, NULL, hole_counting_callback, &hcs);
770 
771   if (plated != NULL)   *plated   = hcs.nplated;
772   if (unplated != NULL) *unplated = hcs.nunplated;
773 }
774 
775 /*!
776  * \brief Counts the number of plated and unplated holes in the design
777  * within a given area of the board.
778  *
779  * To count for the whole board, pass NULL to the within_area.
780  */
781 void
CountHolesEx(int * plated,int * unplated,const BoxType * within_area,Cardinal group_from,Cardinal group_to)782 CountHolesEx (int *plated, int *unplated, const BoxType *within_area, Cardinal group_from, Cardinal group_to)
783 {
784   HoleCountStruct hcs = {0, 0, group_from, group_to};
785 
786   r_search (PCB->Data->pin_tree, within_area, NULL, hole_counting_callback, &hcs);
787   r_search (PCB->Data->via_tree, within_area, NULL, hole_counting_callback, &hcs);
788 
789   if (plated != NULL)   *plated   = hcs.nplated;
790   if (unplated != NULL) *unplated = hcs.nunplated;
791 }
792 
793 /*!
794  * \brief Gets minimum and maximum coordinates.
795  *
796  * \return NULL if layout is empty.
797  */
798 BoxType *
GetDataBoundingBox(DataType * Data)799 GetDataBoundingBox (DataType *Data)
800 {
801   static BoxType box;
802   /* FIX ME: use r_search to do this much faster */
803 
804   /* preset identifiers with highest and lowest possible values */
805   box.X1 = box.Y1 = MAX_COORD;
806   box.X2 = box.Y2 = -MAX_COORD;
807 
808   /* now scan for the lowest/highest X and Y coordinate */
809   VIA_LOOP (Data);
810   {
811     box.X1 = MIN (box.X1, via->X - via->Thickness / 2);
812     box.Y1 = MIN (box.Y1, via->Y - via->Thickness / 2);
813     box.X2 = MAX (box.X2, via->X + via->Thickness / 2);
814     box.Y2 = MAX (box.Y2, via->Y + via->Thickness / 2);
815   }
816   END_LOOP;
817   ELEMENT_LOOP (Data);
818   {
819     box.X1 = MIN (box.X1, element->BoundingBox.X1);
820     box.Y1 = MIN (box.Y1, element->BoundingBox.Y1);
821     box.X2 = MAX (box.X2, element->BoundingBox.X2);
822     box.Y2 = MAX (box.Y2, element->BoundingBox.Y2);
823     {
824       TextType *text = &NAMEONPCB_TEXT (element);
825       box.X1 = MIN (box.X1, text->BoundingBox.X1);
826       box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
827       box.X2 = MAX (box.X2, text->BoundingBox.X2);
828       box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
829     };
830   }
831   END_LOOP;
832   ALLLINE_LOOP (Data);
833   {
834     box.X1 = MIN (box.X1, line->Point1.X - line->Thickness / 2);
835     box.Y1 = MIN (box.Y1, line->Point1.Y - line->Thickness / 2);
836     box.X1 = MIN (box.X1, line->Point2.X - line->Thickness / 2);
837     box.Y1 = MIN (box.Y1, line->Point2.Y - line->Thickness / 2);
838     box.X2 = MAX (box.X2, line->Point1.X + line->Thickness / 2);
839     box.Y2 = MAX (box.Y2, line->Point1.Y + line->Thickness / 2);
840     box.X2 = MAX (box.X2, line->Point2.X + line->Thickness / 2);
841     box.Y2 = MAX (box.Y2, line->Point2.Y + line->Thickness / 2);
842   }
843   ENDALL_LOOP;
844   ALLARC_LOOP (Data);
845   {
846     box.X1 = MIN (box.X1, arc->BoundingBox.X1);
847     box.Y1 = MIN (box.Y1, arc->BoundingBox.Y1);
848     box.X2 = MAX (box.X2, arc->BoundingBox.X2);
849     box.Y2 = MAX (box.Y2, arc->BoundingBox.Y2);
850   }
851   ENDALL_LOOP;
852   ALLTEXT_LOOP (Data);
853   {
854     box.X1 = MIN (box.X1, text->BoundingBox.X1);
855     box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
856     box.X2 = MAX (box.X2, text->BoundingBox.X2);
857     box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
858   }
859   ENDALL_LOOP;
860   ALLPOLYGON_LOOP (Data);
861   {
862     box.X1 = MIN (box.X1, polygon->BoundingBox.X1);
863     box.Y1 = MIN (box.Y1, polygon->BoundingBox.Y1);
864     box.X2 = MAX (box.X2, polygon->BoundingBox.X2);
865     box.Y2 = MAX (box.Y2, polygon->BoundingBox.Y2);
866   }
867   ENDALL_LOOP;
868   return (IsDataEmpty (Data) ? NULL : &box);
869 }
870 
871 /*!
872  * \brief Centers the displayed PCB around the specified point (X,Y),
873  * and move the crosshair there.
874  *
875  * If warp_pointer is true warp the pointer to the crosshair.
876  */
877 void
CenterDisplay(Coord X,Coord Y,bool warp_pointer)878 CenterDisplay (Coord X, Coord Y, bool warp_pointer)
879 {
880   Coord save_grid = PCB->Grid;
881 
882   PCB->Grid = 1;
883 
884   if (MoveCrosshairAbsolute (X, Y))
885     notify_crosshair_change (true);
886 
887   if (warp_pointer)
888     gui->set_crosshair (Crosshair.X, Crosshair.Y,
889                         HID_SC_CENTER_IN_VIEWPORT_AND_WARP_POINTER );
890   else
891     gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_CENTER_IN_VIEWPORT);
892 
893   PCB->Grid = save_grid;
894 }
895 
896 /*!
897  * \brief Transforms symbol coordinates so that the left edge of each
898  * symbol is at the zero position.
899  *
900  * The y coordinates are moved so that min(y) = 0.
901  *
902  */
903 void
SetFontInfo(FontType * Ptr)904 SetFontInfo (FontType *Ptr)
905 {
906   Cardinal i, j;
907   SymbolType *symbol;
908   LineType *line;
909   Coord totalminy = MAX_COORD;
910 
911   /* calculate cell with and height (is at least DEFAULT_CELLSIZE)
912    * maximum cell width and height
913    * minimum x and y position of all lines
914    */
915   Ptr->MaxWidth = DEFAULT_CELLSIZE;
916   Ptr->MaxHeight = DEFAULT_CELLSIZE;
917   for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
918     {
919       Coord minx, miny, maxx, maxy;
920 
921       /* next one if the index isn't used or symbol is empty (SPACE) */
922       if (!symbol->Valid || !symbol->LineN)
923         continue;
924 
925       minx = miny = MAX_COORD;
926       maxx = maxy = 0;
927       for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
928         {
929           minx = MIN (minx, line->Point1.X);
930           miny = MIN (miny, line->Point1.Y);
931           minx = MIN (minx, line->Point2.X);
932           miny = MIN (miny, line->Point2.Y);
933           maxx = MAX (maxx, line->Point1.X);
934           maxy = MAX (maxy, line->Point1.Y);
935           maxx = MAX (maxx, line->Point2.X);
936           maxy = MAX (maxy, line->Point2.Y);
937         }
938 
939       /* move symbol to left edge */
940       for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
941         MOVE_LINE_LOWLEVEL (line, -minx, 0);
942 
943       /* set symbol bounding box with a minimum cell size of (1,1) */
944       symbol->Width = maxx - minx + 1;
945       symbol->Height = maxy + 1;
946 
947       /* check total min/max  */
948       Ptr->MaxWidth = MAX (Ptr->MaxWidth, symbol->Width);
949       Ptr->MaxHeight = MAX (Ptr->MaxHeight, symbol->Height);
950       totalminy = MIN (totalminy, miny);
951     }
952 
953   /* move coordinate system to the upper edge (lowest y on screen) */
954   for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
955     if (symbol->Valid)
956       {
957         symbol->Height -= totalminy;
958         for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
959           MOVE_LINE_LOWLEVEL (line, 0, -totalminy);
960       }
961 
962   /* setup the box for the default symbol */
963   Ptr->DefaultSymbol.X1 = Ptr->DefaultSymbol.Y1 = 0;
964   Ptr->DefaultSymbol.X2 = Ptr->DefaultSymbol.X1 + Ptr->MaxWidth;
965   Ptr->DefaultSymbol.Y2 = Ptr->DefaultSymbol.Y1 + Ptr->MaxHeight;
966 }
967 
968 static Coord
GetNum(char ** s,const char * default_unit)969 GetNum (char **s, const char *default_unit)
970 {
971   /* Read value */
972   Coord ret_val = GetValueEx (*s, NULL, NULL, NULL, default_unit);
973   /* Advance pointer */
974   while(isalnum(**s) || **s == '.')
975      (*s)++;
976   return ret_val;
977 }
978 
979 /*!
980  * \brief Serializes the route style list .
981  *
982  *  Right now n_styles should always be set to NUM_STYLES,
983  *  since that is the number of route styles ParseRouteString()
984  *  expects to parse.
985  */
986 char *
make_route_string(RouteStyleType rs[],int n_styles)987 make_route_string (RouteStyleType rs[], int n_styles)
988 {
989   GString *str = g_string_new ("");
990   gint i;
991 
992   for (i = 0; i < n_styles; ++i)
993     {
994       char *r_string;
995       // don't much like how this is done, but it'll have to do for now
996       if (rs[i].ViaMask != 0)
997         r_string = pcb_g_strdup_printf ("%s,%$mr,%$mr,%$mr,%$mr,%$mr",
998                                             rs[i].Name,
999                                             rs[i].Thick, rs[i].Diameter,
1000                                             rs[i].Hole, rs[i].Keepaway,
1001                                             rs[i].ViaMask);
1002       else
1003         r_string = pcb_g_strdup_printf ("%s,%$mr,%$mr,%$mr,%$mr", rs[i].Name,
1004                                         rs[i].Thick, rs[i].Diameter,
1005                                         rs[i].Hole, rs[i].Keepaway);
1006       if (i > 0)
1007         g_string_append_c (str, ':');
1008       g_string_append (str, r_string);
1009       g_free (r_string);
1010     }
1011   return g_string_free (str, FALSE);
1012 }
1013 
1014 /*!
1015  * \brief Parses the routes definition string
1016  *
1017  * Which is a colon separated list of comma separated Name, Dimension,
1018  * Dimension, Dimension[, Dimension, Dimension].
1019  *
1020  * \example Signal,20,40,20,10,0:Power,40,60,28,10,0:...
1021  */
1022 int
ParseRouteString(char * s,RouteStyleType * routeStyle,const char * default_unit)1023 ParseRouteString (char *s, RouteStyleType *routeStyle, const char *default_unit)
1024 {
1025   int i, n, style;
1026   char Name[256];
1027   char *orig_s = s;
1028 
1029   Coord *style_items[5];
1030 
1031   style = 0;
1032   while (style < NUM_STYLES)
1033     {
1034       // Clear any existing data
1035       memset (routeStyle, 0, sizeof (RouteStyleType));
1036       // Extract the name
1037       while (*s && isspace ((int) *s))  s++;
1038       // this will break for style names greater than 256 characters
1039       for (i = 0; *s && *s != ','; i++)  Name[i] = *s++;
1040       Name[i] = '\0';
1041       routeStyle->Name = strdup (Name);
1042 
1043       // setup some default values
1044       routeStyle->Keepaway = MIL_TO_COORD(10);
1045       routeStyle->ViaMask = 0;
1046 
1047       // Now extract the numerical parameters
1048       style_items[0] = &(routeStyle->Thick);
1049       style_items[1] = &(routeStyle->Diameter);
1050       style_items[2] = &(routeStyle->Hole);
1051       style_items[3] = &(routeStyle->Keepaway);
1052       style_items[4] = &(routeStyle->ViaMask);
1053       for (n = 0; n < 5; n++)
1054         {
1055         if (!isdigit ((int) *++s))  goto error;
1056         *style_items[n] = GetNum(&s, default_unit);
1057         // find the next field, skipping any white space
1058         while (*s && isspace ((int) *s))  s++;
1059         if (*s != ',')  break;
1060         while (*s && isspace ((int) *s))  s++;
1061         }
1062 
1063       // how did we get out of the loop?
1064       // if we're at the end of the string, we're done
1065       if (*s == '\0')  break;
1066       // there's another route style to parse, advance past the delimiter
1067       else if (*s == ':')  s++;
1068       // otherwise, we got an unexpected character, which is an error
1069       else {
1070         fprintf(stderr, "unexpected character: %c\n", *s);
1071         goto error;
1072       }
1073 
1074       // otherwise, prepare for the next loop
1075       style++; routeStyle++;
1076     }
1077   return (0);
1078 
1079 error:
1080   fprintf(stderr, "error parsing route string: %s\n", orig_s);
1081   fprintf(stderr, "parsed %ld characters.\n", s - orig_s);
1082   fprintf(stderr, "on style number %d\n", style+1);
1083   fprintf(stderr, "character that caused the error: %c\n", *s);
1084   fprintf(stderr, "values of current struct: \n");
1085   for (n=0; n < 5; n++) fprintf(stderr, "%d: %ld ", n, *style_items[n]);
1086   memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
1087   return (1);
1088 }
1089 
1090 /*!
1091  * \brief Parses the group definition string.
1092  *
1093  * Which is a colon separated list of comma separated layer numbers.
1094  *
1095  * \example (1,2,b:4,6,8,t)
1096  */
1097 int
ParseGroupString(char * group_string,LayerGroupType * LayerGroup,int * LayerN)1098 ParseGroupString (char *group_string, LayerGroupType *LayerGroup, int *LayerN)
1099 {
1100   char *s;
1101   int group, member, layer;
1102   bool c_set = false,        /* flags for the two special layers to */
1103     s_set = false;              /* provide a default setting for old formats */
1104   int groupnum[MAX_ALL_LAYER];
1105 
1106   *LayerN = 0;
1107 
1108   /* Deterimine the maximum layer number */
1109   for (s = group_string; s && *s; s++)
1110     {
1111       while (*s && isspace ((int) *s))
1112         s++;
1113 
1114       switch (*s)
1115         {
1116         case 'c':
1117         case 'C':
1118         case 't':
1119         case 'T':
1120         case 's':
1121         case 'S':
1122         case 'b':
1123         case 'B':
1124           break;
1125 
1126         default:
1127           if (!isdigit ((int) *s))
1128             goto error;
1129           *LayerN = MAX (*LayerN, atoi (s));
1130           break;
1131         }
1132 
1133       while (*++s && isdigit ((int) *s));
1134 
1135       /* ignore white spaces and check for separator */
1136       while (*s && isspace ((int) *s))
1137         s++;
1138 
1139       if (*s == '\0')
1140         break;
1141 
1142       if (*s != ':' && *s != ',')
1143         goto error;
1144     }
1145 
1146   /* clear struct */
1147   memset (LayerGroup, 0, sizeof (LayerGroupType));
1148 
1149   /* Clear assignments */
1150   for (layer = 0; layer < MAX_ALL_LAYER; layer++)
1151     groupnum[layer] = -1;
1152 
1153   /* loop over all groups */
1154   for (s = group_string, group = 0;
1155        s && *s && group < *LayerN;
1156        group++)
1157     {
1158       while (*s && isspace ((int) *s))
1159         s++;
1160 
1161       /* loop over all group members */
1162       for (member = 0; *s; s++)
1163         {
1164           /* ignore white spaces and get layernumber */
1165           while (*s && isspace ((int) *s))
1166             s++;
1167           switch (*s)
1168             {
1169             case 'c':
1170             case 'C':
1171             case 't':
1172             case 'T':
1173               layer = *LayerN + TOP_SILK_LAYER;
1174               c_set = true;
1175               break;
1176 
1177             case 's':
1178             case 'S':
1179             case 'b':
1180             case 'B':
1181               layer = *LayerN + BOTTOM_SILK_LAYER;
1182               s_set = true;
1183               break;
1184 
1185             default:
1186               layer = atoi (s) - 1;
1187               break;
1188             }
1189           if (layer > *LayerN + MAX (BOTTOM_SILK_LAYER, TOP_SILK_LAYER) ||
1190               member >= *LayerN + 1)
1191             goto error;
1192           groupnum[layer] = group;
1193           LayerGroup->Entries[group][member++] = layer;
1194           while (*++s && isdigit ((int) *s));
1195 
1196           /* ignore white spaces and check for separator */
1197           while (*s && isspace ((int) *s))
1198             s++;
1199           if (!*s || *s == ':')
1200             break;
1201         }
1202       LayerGroup->Number[group] = member;
1203       if (*s == ':')
1204         s++;
1205     }
1206 
1207   /* If no explicit solder or component layer group was found in the layer
1208    * group string, make group 0 the bottom side, and group 1 the top side.
1209    * This is done by assigning the relevant silkscreen layers to those groups.
1210    */
1211   if (!s_set)
1212     LayerGroup->Entries[0][LayerGroup->Number[0]++] = *LayerN + BOTTOM_SILK_LAYER;
1213   if (!c_set)
1214     LayerGroup->Entries[1][LayerGroup->Number[1]++] = *LayerN + TOP_SILK_LAYER;
1215 
1216   /* Assign a unique layer group to each layer that was not explicitly
1217    * assigned a particular group by its presence in the layer group string.
1218    */
1219   for (layer = 0; layer < *LayerN && group < *LayerN; layer++)
1220     if (groupnum[layer] == -1)
1221       {
1222         LayerGroup->Entries[group][0] = layer;
1223         LayerGroup->Number[group] = 1;
1224         group++;
1225       }
1226   return (0);
1227 
1228   /* reset structure on error */
1229 error:
1230   memset (LayerGroup, 0, sizeof (LayerGroupType));
1231   return (1);
1232 }
1233 
1234 /*!
1235  * \brief Sets up any remaining layer type guesses.
1236  */
1237 void
AssignDefaultLayerTypes()1238 AssignDefaultLayerTypes()
1239 {
1240   int num_found;
1241   Cardinal outline_layer = -1;
1242 
1243   /*
1244    * There can be only one outline layer. During parsing guess_layertype()
1245    * applied well known cases already, but as this function operates on a
1246    * single layer only, it might end up with more than one hit for the whole
1247    * file. Especially after loading an older layout without saved flags.
1248    */
1249   num_found = 0;
1250   LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
1251     outline_layer = n;
1252     num_found++;
1253   END_LOOP;
1254 
1255   if (num_found != 1)
1256     /* No or duplicate outline! Try to find a layer which is named exactly
1257        "outline". */
1258     LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
1259       if ( ! strcasecmp (layer->Name, "outline"))
1260         {
1261           outline_layer = n;
1262           num_found = 1;
1263           break;
1264         }
1265     END_LOOP;
1266 
1267   if (num_found != 1)
1268     /* Next, try to find a layer which is named exactly "route". */
1269     LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
1270       if ( ! strcasecmp (layer->Name, "route"))
1271         {
1272           outline_layer = n;
1273           num_found = 1;
1274           break;
1275         }
1276     END_LOOP;
1277 
1278   if (num_found != 1)
1279     /* As last resort, take the first layer claiming to be outline. */
1280     LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
1281       outline_layer = n;
1282       num_found = 1;
1283       break;
1284     END_LOOP;
1285 
1286   /* Make sure our found outline layer is the only one. */
1287   LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
1288     if (n == outline_layer)
1289       layer->Type = LT_OUTLINE;
1290     else
1291       layer->Type = LT_ROUTE;  /* best guess */
1292   END_LOOP;
1293 }
1294 
1295 extern void pcb_main_uninit(void);
1296 
1297 /*!
1298  * \brief Quits application.
1299  */
1300 void
QuitApplication(void)1301 QuitApplication (void)
1302 {
1303   /*
1304    * save data if necessary.  It not needed, then don't trigger EmergencySave
1305    * via our atexit() registering of EmergencySave().  We presumeably wanted to
1306    * exit here and thus it is not an emergency.
1307    */
1308   if (PCB->Changed && Settings.SaveInTMP)
1309     EmergencySave ();
1310   else
1311     DisableEmergencySave ();
1312 
1313   if (gui->do_exit == NULL)
1314     {
1315       pcb_main_uninit ();
1316       exit (0);
1317     }
1318   else
1319     gui->do_exit (gui);
1320 }
1321 
1322 /*!
1323  * \brief Creates a filename from a template.
1324  *
1325  * "%f" is replaced by the filename.
1326  *
1327  * "%p" is replaced by the searchpath.
1328  */
1329 char *
EvaluateFilename(char * Template,char * Path,char * Filename,char * Parameter)1330 EvaluateFilename (char *Template, char *Path, char *Filename, char *Parameter)
1331 {
1332   static DynamicStringType command;
1333   char *p;
1334 
1335   if (Settings.verbose)
1336     {
1337       printf ("EvaluateFilename:\n");
1338       printf ("\tTemplate: \033[33m%s\033[0m\n", Template);
1339       printf ("\tPath: \033[33m%s\033[0m\n", Path);
1340       printf ("\tFilename: \033[33m%s\033[0m\n", Filename);
1341       printf ("\tParameter: \033[33m%s\033[0m\n", Parameter);
1342     }
1343 
1344   DSClearString (&command);
1345 
1346   for (p = Template; p && *p; p++)
1347     {
1348       /* copy character or add string to command */
1349       if (*p == '%'
1350           && (*(p + 1) == 'f' || *(p + 1) == 'p' || *(p + 1) == 'a'))
1351         switch (*(++p))
1352           {
1353           case 'a':
1354             DSAddString (&command, Parameter);
1355             break;
1356           case 'f':
1357             DSAddString (&command, Filename);
1358             break;
1359           case 'p':
1360             DSAddString (&command, Path);
1361             break;
1362           }
1363       else
1364         DSAddCharacter (&command, *p);
1365     }
1366   DSAddCharacter (&command, '\0');
1367   if (Settings.verbose)
1368     printf ("EvaluateFilename: \033[32m%s\033[0m\n", command.Data);
1369 
1370   return strdup (command.Data);
1371 }
1372 
1373 /*!
1374  * \brief Concatenates directory and filename.
1375  *
1376  * If directory != NULL expands them with a shell and returns the found
1377  * name(s) or NULL.
1378  */
1379 char *
ExpandFilename(char * Dirname,char * Filename)1380 ExpandFilename (char *Dirname, char *Filename)
1381 {
1382   static DynamicStringType answer;
1383   char *command;
1384   FILE *pipe;
1385   int c;
1386 
1387   /* allocate memory for commandline and build it */
1388   DSClearString (&answer);
1389   if (Dirname)
1390     {
1391       command = (char *)calloc (strlen (Filename) + strlen (Dirname) + 7,
1392                         sizeof (char));
1393       sprintf (command, "echo %s/%s", Dirname, Filename);
1394     }
1395   else
1396     {
1397       command = (char *)calloc (strlen (Filename) + 6, sizeof (char));
1398       sprintf (command, "echo %s", Filename);
1399     }
1400 
1401   /* execute it with shell */
1402   if ((pipe = popen (command, "r")) != NULL)
1403     {
1404       /* discard all but the first returned line */
1405       for (;;)
1406         {
1407           if ((c = fgetc (pipe)) == EOF || c == '\n' || c == '\r')
1408             break;
1409           else
1410             DSAddCharacter (&answer, c);
1411         }
1412 
1413       free (command);
1414       return (pclose (pipe) ? NULL : answer.Data);
1415     }
1416 
1417   /* couldn't be expanded by the shell */
1418   PopenErrorMessage (command);
1419   free (command);
1420   return (NULL);
1421 }
1422 
1423 
1424 /*!
1425  * \brief Returns the layer number for the passed pointer.
1426  */
1427 int
GetLayerNumber(DataType * Data,LayerType * Layer)1428 GetLayerNumber (DataType *Data, LayerType *Layer)
1429 {
1430   int i;
1431 
1432   for (i = 0; i < MAX_ALL_LAYER; i++)
1433     if (Layer == &Data->Layer[i])
1434       break;
1435   return (i);
1436 }
1437 
1438 /*!
1439  * \brief Move layer (number is passed in) to top of layerstack.
1440  */
1441 static void
PushOnTopOfLayerStack(int NewTop)1442 PushOnTopOfLayerStack (int NewTop)
1443 {
1444   int i;
1445 
1446   /* ignore silk and other extra layers */
1447   if (NewTop < max_copper_layer)
1448     {
1449       /* first find position of passed one */
1450       for (i = 0; i < max_copper_layer; i++)
1451         if (LayerStack[i] == NewTop)
1452           break;
1453 
1454       /* bring this element to the top of the stack */
1455       for (; i; i--)
1456         LayerStack[i] = LayerStack[i - 1];
1457       LayerStack[0] = NewTop;
1458     }
1459 }
1460 
1461 
1462 /*!
1463  * \brief Changes the visibility of all layers in a group.
1464  *
1465  * \return The number of changed layers.
1466  */
1467 int
ChangeGroupVisibility(int Layer,bool On,bool ChangeStackOrder)1468 ChangeGroupVisibility (int Layer, bool On, bool ChangeStackOrder)
1469 {
1470   int group, i, changed = 1;    /* at least the current layer changes */
1471 
1472   /* Warning: these special case values must agree with what gui-top-window.c
1473      |  thinks the are.
1474    */
1475 
1476   if (Settings.verbose)
1477     printf ("ChangeGroupVisibility(Layer=%d, On=%d, ChangeStackOrder=%d)\n",
1478             Layer, On, ChangeStackOrder);
1479 
1480   /* decrement 'i' to keep stack in order of layergroup */
1481   group = GetLayerGroupNumberByNumber (Layer);
1482   for (i = PCB->LayerGroups.Number[group]; i;)
1483     {
1484       int layer = PCB->LayerGroups.Entries[group][--i];
1485 
1486       /* don't count the passed member of the group */
1487       if (layer != Layer && layer < max_copper_layer)
1488         {
1489           PCB->Data->Layer[layer].On = On;
1490 
1491           /* push layer on top of stack if switched on */
1492           if (On && ChangeStackOrder)
1493             PushOnTopOfLayerStack (layer);
1494           changed++;
1495         }
1496     }
1497 
1498   /* change at least the passed layer */
1499   PCB->Data->Layer[Layer].On = On;
1500   if (On && ChangeStackOrder)
1501     PushOnTopOfLayerStack (Layer);
1502 
1503   /* update control panel and exit */
1504   hid_action ("LayersChanged");
1505   return (changed);
1506 }
1507 
1508 /*!
1509  * \brief Given a string description of a layer stack, adjust the layer
1510  * stack to correspond.
1511  */
1512 void
LayerStringToLayerStack(char * s)1513 LayerStringToLayerStack (char *s)
1514 {
1515   static int listed_layers = 0;
1516   int l = strlen (s);
1517   char **args;
1518   int i, argn, lno;
1519   int prev_sep = 1;
1520 
1521   s = strdup (s);
1522   args = (char **) malloc (l * sizeof (char *));
1523   argn = 0;
1524 
1525   for (i=0; i<l; i++)
1526     {
1527       switch (s[i])
1528 	{
1529 	case ' ':
1530 	case '\t':
1531 	case ',':
1532 	case ';':
1533 	case ':':
1534 	  prev_sep = 1;
1535 	  s[i] = '\0';
1536 	  break;
1537 	default:
1538 	  if (prev_sep)
1539 	    args[argn++] = s+i;
1540 	  prev_sep = 0;
1541 	  break;
1542 	}
1543     }
1544 
1545   for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
1546     {
1547       if (i < max_copper_layer)
1548         LayerStack[i] = i;
1549       PCB->Data->Layer[i].On = false;
1550     }
1551   PCB->ElementOn = false;
1552   PCB->InvisibleObjectsOn = false;
1553   PCB->PinOn = false;
1554   PCB->ViaOn = false;
1555   PCB->RatOn = false;
1556   CLEAR_FLAG (SHOWMASKFLAG, PCB);
1557   Settings.ShowBottomSide = 0;
1558 
1559   for (i=argn-1; i>=0; i--)
1560     {
1561       if (strcasecmp (args[i], "rats") == 0)
1562 	PCB->RatOn = true;
1563       else if (strcasecmp (args[i], "invisible") == 0)
1564 	PCB->InvisibleObjectsOn = true;
1565       else if (strcasecmp (args[i], "pins") == 0)
1566 	PCB->PinOn = true;
1567       else if (strcasecmp (args[i], "vias") == 0)
1568 	PCB->ViaOn = true;
1569       else if (strcasecmp (args[i], "elements") == 0
1570 	       || strcasecmp (args[i], "silk") == 0)
1571 	PCB->ElementOn = true;
1572       else if (strcasecmp (args[i], "mask") == 0)
1573 	SET_FLAG (SHOWMASKFLAG, PCB);
1574       else if (strcasecmp (args[i], "solderside") == 0)
1575 	Settings.ShowBottomSide = 1;
1576       else if (isdigit ((int) args[i][0]))
1577 	{
1578 	  lno = atoi (args[i]);
1579 	  ChangeGroupVisibility (lno, true, true);
1580 	}
1581       else
1582 	{
1583 	  int found = 0;
1584 	  for (lno = 0; lno < max_copper_layer; lno++)
1585 	    if (strcasecmp (args[i], PCB->Data->Layer[lno].Name) == 0)
1586 	      {
1587 		ChangeGroupVisibility (lno, true, true);
1588 		found = 1;
1589 		break;
1590 	      }
1591 	  if (!found)
1592 	    {
1593 	      fprintf(stderr, _("Warning: layer \"%s\" not known\n"), args[i]);
1594 	      if (!listed_layers)
1595 		{
1596 		  fprintf (stderr, _("Named layers in this board are:\n"));
1597 		  listed_layers = 1;
1598 		  for (lno=0; lno < max_copper_layer; lno ++)
1599 		    fprintf(stderr, "\t%s\n", PCB->Data->Layer[lno].Name);
1600 		  fprintf(stderr, _("Also: component, solder, rats, invisible, "
1601 			"pins, vias, elements or silk, mask, solderside.\n"));
1602 		}
1603 	    }
1604 	}
1605     }
1606 }
1607 
1608 /*!
1609  * \brief Returns the layergroup number for the passed pointer.
1610  */
1611 int
GetLayerGroupNumberByPointer(LayerType * Layer)1612 GetLayerGroupNumberByPointer (LayerType *Layer)
1613 {
1614   return (GetLayerGroupNumberByNumber (GetLayerNumber (PCB->Data, Layer)));
1615 }
1616 
1617 /*!
1618  * \brief Returns the layergroup number for the passed layer number.
1619  */
1620 int
GetLayerGroupNumberByNumber(Cardinal Layer)1621 GetLayerGroupNumberByNumber (Cardinal Layer)
1622 {
1623   int group, entry;
1624 
1625   for (group = 0; group < max_group; group++)
1626     for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
1627       if (PCB->LayerGroups.Entries[group][entry] == Layer)
1628         return (group);
1629 
1630   /* since every layer belongs to a group it is safe to return
1631    * the value without boundary checking
1632    */
1633   return (group);
1634 }
1635 
1636 /*!
1637  * \brief Returns the layergroup number for the passed side (TOP_SIDE or
1638  * BOTTOM_SIDE).
1639  */
1640 int
GetLayerGroupNumberBySide(int side)1641 GetLayerGroupNumberBySide (int side)
1642 {
1643   /* Find the relavant board side layer group by determining the
1644    * layer group associated with the relevant side's silk-screen
1645    */
1646   return GetLayerGroupNumberByNumber (
1647       side == TOP_SIDE ? top_silk_layer : bottom_silk_layer);
1648 }
1649 
1650 /*!
1651  * \brief Returns a pointer to an objects bounding box.
1652  *
1653  * Data is valid until the routine is called again.
1654  */
1655 BoxType *
GetObjectBoundingBox(int Type,void * Ptr1,void * Ptr2,void * Ptr3)1656 GetObjectBoundingBox (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1657 {
1658   switch (Type)
1659     {
1660     case LINE_TYPE:
1661     case ARC_TYPE:
1662     case TEXT_TYPE:
1663     case POLYGON_TYPE:
1664     case PAD_TYPE:
1665     case PIN_TYPE:
1666     case ELEMENTNAME_TYPE:
1667       return (BoxType *)Ptr2;
1668     case VIA_TYPE:
1669     case ELEMENT_TYPE:
1670       return (BoxType *)Ptr1;
1671     case POLYGONPOINT_TYPE:
1672     case LINEPOINT_TYPE:
1673       return (BoxType *)Ptr3;
1674     default:
1675       Message (_("Request for bounding box of unsupported type %d\n"), Type);
1676       return (BoxType *)Ptr2;
1677     }
1678 }
1679 
1680 /*!
1681  * \brief Computes the bounding box of an arc.
1682  */
1683 void
SetArcBoundingBox(ArcType * Arc)1684 SetArcBoundingBox (ArcType *Arc)
1685 {
1686   double ca1, ca2, sa1, sa2;
1687   double minx, maxx, miny, maxy;
1688   Angle ang1, ang2;
1689   Coord width;
1690 
1691   /* first put angles into standard form:
1692    *  ang1 < ang2, both angles between 0 and 720 */
1693   Arc->Delta = CLAMP (Arc->Delta, -360, 360);
1694 
1695   if (Arc->Delta > 0)
1696     {
1697       ang1 = NormalizeAngle (Arc->StartAngle);
1698       ang2 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
1699     }
1700   else
1701     {
1702       ang1 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
1703       ang2 = NormalizeAngle (Arc->StartAngle);
1704     }
1705   if (ang1 > ang2)
1706     ang2 += 360;
1707   /* Make sure full circles aren't treated as zero-length arcs */
1708   if (Arc->Delta == 360 || Arc->Delta == -360)
1709     ang2 = ang1 + 360;
1710 
1711   /* calculate sines, cosines */
1712   sa1 = sin (M180 * ang1);
1713   ca1 = cos (M180 * ang1);
1714   sa2 = sin (M180 * ang2);
1715   ca2 = cos (M180 * ang2);
1716 
1717   minx = MIN (ca1, ca2);
1718   maxx = MAX (ca1, ca2);
1719   miny = MIN (sa1, sa2);
1720   maxy = MAX (sa1, sa2);
1721 
1722   /* Check for extreme angles */
1723   if ((ang1 <= 0   && ang2 >= 0)   || (ang1 <= 360 && ang2 >= 360)) maxx = 1;
1724   if ((ang1 <= 90  && ang2 >= 90)  || (ang1 <= 450 && ang2 >= 450)) maxy = 1;
1725   if ((ang1 <= 180 && ang2 >= 180) || (ang1 <= 540 && ang2 >= 540)) minx = -1;
1726   if ((ang1 <= 270 && ang2 >= 270) || (ang1 <= 630 && ang2 >= 630)) miny = -1;
1727 
1728   /* Finally, calcate bounds, converting sane geometry into pcb geometry */
1729   Arc->BoundingBox.X1 = Arc->X - Arc->Width * maxx;
1730   Arc->BoundingBox.X2 = Arc->X - Arc->Width * minx;
1731   Arc->BoundingBox.Y1 = Arc->Y + Arc->Height * miny;
1732   Arc->BoundingBox.Y2 = Arc->Y + Arc->Height * maxy;
1733 
1734   width = (Arc->Thickness + Arc->Clearance) / 2;
1735 
1736   /* Adjust for our discrete polygon approximation */
1737   width = (double)width * MAX (POLY_CIRC_RADIUS_ADJ, (1.0 + POLY_ARC_MAX_DEVIATION)) + 0.5;
1738 
1739   Arc->BoundingBox.X1 -= width;
1740   Arc->BoundingBox.X2 += width;
1741   Arc->BoundingBox.Y1 -= width;
1742   Arc->BoundingBox.Y2 += width;
1743   close_box(&Arc->BoundingBox);
1744 
1745   /* Update the arc end-points */
1746   Arc->Point1.X = Arc->X - (double)Arc->Width  * ca1;
1747   Arc->Point1.Y = Arc->Y + (double)Arc->Height * sa1;
1748   Arc->Point2.X = Arc->X - (double)Arc->Width  * ca2;
1749   Arc->Point2.Y = Arc->Y + (double)Arc->Height * sa2;
1750 }
1751 
1752 /*!
1753  * \brief Resets the layerstack setting.
1754  */
1755 void
ResetStackAndVisibility(void)1756 ResetStackAndVisibility (void)
1757 {
1758   int top_group;
1759   Cardinal i;
1760 
1761   for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
1762     {
1763       if (i < max_copper_layer)
1764         LayerStack[i] = i;
1765       PCB->Data->Layer[i].On = true;
1766     }
1767   PCB->ElementOn = true;
1768   PCB->InvisibleObjectsOn = true;
1769   PCB->PinOn = true;
1770   PCB->ViaOn = true;
1771   PCB->RatOn = true;
1772 
1773   /* Bring the component group to the front and make it active.  */
1774   top_group = GetLayerGroupNumberBySide (TOP_SIDE);
1775   ChangeGroupVisibility (PCB->LayerGroups.Entries[top_group][0], 1, 1);
1776 }
1777 
1778 /*!
1779  * \brief Saves the layerstack setting.
1780  */
1781 void
SaveStackAndVisibility(void)1782 SaveStackAndVisibility (void)
1783 {
1784   Cardinal i;
1785   static bool run = false;
1786 
1787   if (run == false)
1788     {
1789       SavedStack.cnt = 0;
1790       run = true;
1791     }
1792 
1793   if (SavedStack.cnt != 0)
1794     {
1795       fprintf (stderr,
1796                "SaveStackAndVisibility()  layerstack was already saved and not"
1797                "yet restored.  cnt = %d\n", SavedStack.cnt);
1798     }
1799 
1800   for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
1801     {
1802       if (i < max_copper_layer)
1803         SavedStack.LayerStack[i] = LayerStack[i];
1804       SavedStack.LayerOn[i] = PCB->Data->Layer[i].On;
1805     }
1806   SavedStack.ElementOn = PCB->ElementOn;
1807   SavedStack.InvisibleObjectsOn = PCB->InvisibleObjectsOn;
1808   SavedStack.PinOn = PCB->PinOn;
1809   SavedStack.ViaOn = PCB->ViaOn;
1810   SavedStack.RatOn = PCB->RatOn;
1811   SavedStack.cnt++;
1812 }
1813 
1814 /*!
1815  * \brief Restores the layerstack setting.
1816  */
1817 void
RestoreStackAndVisibility(void)1818 RestoreStackAndVisibility (void)
1819 {
1820   Cardinal i;
1821 
1822   if (SavedStack.cnt == 0)
1823     {
1824       fprintf (stderr, "RestoreStackAndVisibility()  layerstack has not"
1825                " been saved.  cnt = %d\n", SavedStack.cnt);
1826       return;
1827     }
1828   else if (SavedStack.cnt != 1)
1829     {
1830       fprintf (stderr, "RestoreStackAndVisibility()  layerstack save count is"
1831                " wrong.  cnt = %d\n", SavedStack.cnt);
1832     }
1833 
1834   for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
1835     {
1836       if (i < max_copper_layer)
1837         LayerStack[i] = SavedStack.LayerStack[i];
1838       PCB->Data->Layer[i].On = SavedStack.LayerOn[i];
1839     }
1840   PCB->ElementOn = SavedStack.ElementOn;
1841   PCB->InvisibleObjectsOn = SavedStack.InvisibleObjectsOn;
1842   PCB->PinOn = SavedStack.PinOn;
1843   PCB->ViaOn = SavedStack.ViaOn;
1844   PCB->RatOn = SavedStack.RatOn;
1845 
1846   SavedStack.cnt--;
1847 }
1848 
1849 /*!
1850  * \brief Returns pointer to current working directory.
1851  *
1852  * If 'path' is not NULL, then the current working directory is copied
1853  * to the array pointed to by 'path'.
1854  */
1855 char *
GetWorkingDirectory(char * path)1856 GetWorkingDirectory (char *path)
1857 {
1858 #ifdef HAVE_GETCWD
1859   return getcwd (path, MAXPATHLEN);
1860 #else
1861   /* seems that some BSD releases lack of a prototype for getwd() */
1862   return getwd (path);
1863 #endif
1864 
1865 }
1866 
1867 /*!
1868  * \brief Write a string to the passed file pointer.
1869  *
1870  * Some special characters are quoted.
1871  */
1872 void
CreateQuotedString(DynamicStringType * DS,char * S)1873 CreateQuotedString (DynamicStringType *DS, char *S)
1874 {
1875   DSClearString (DS);
1876   DSAddCharacter (DS, '"');
1877   while (*S)
1878     {
1879       if (*S == '"' || *S == '\\')
1880         DSAddCharacter (DS, '\\');
1881       DSAddCharacter (DS, *S++);
1882     }
1883   DSAddCharacter (DS, '"');
1884 }
1885 
1886 BoxType *
GetArcEnds(ArcType * Arc)1887 GetArcEnds (ArcType *Arc)
1888 {
1889   static BoxType box;
1890   box.X1 = Arc->X - Arc->Width * cos (Arc->StartAngle * M180);
1891   box.Y1 = Arc->Y + Arc->Height * sin (Arc->StartAngle * M180);
1892   box.X2 = Arc->X - Arc->Width * cos ((Arc->StartAngle + Arc->Delta) * M180);
1893   box.Y2 = Arc->Y + Arc->Height * sin ((Arc->StartAngle + Arc->Delta) * M180);
1894   return &box;
1895 }
1896 
1897 
1898 /*!
1899  * \todo Doesn't this belong in change.c ??
1900  */
1901 void
ChangeArcAngles(LayerType * Layer,ArcType * a,Angle new_sa,Angle new_da)1902 ChangeArcAngles (LayerType *Layer, ArcType *a,
1903                  Angle new_sa, Angle new_da)
1904 {
1905   if (new_da >= 360)
1906     {
1907       new_da = 360;
1908       new_sa = 0;
1909     }
1910   RestoreToPolygon (PCB->Data, ARC_TYPE, Layer, a);
1911   r_delete_entry (Layer->arc_tree, (BoxType *) a);
1912   AddObjectToChangeAnglesUndoList (ARC_TYPE, a, a, a);
1913   a->StartAngle = new_sa;
1914   a->Delta = new_da;
1915   SetArcBoundingBox (a);
1916   r_insert_entry (Layer->arc_tree, (BoxType *) a, 0);
1917   ClearFromPolygon (PCB->Data, ARC_TYPE, Layer, a);
1918 }
1919 
1920 static char *
BumpName(char * Name)1921 BumpName (char *Name)
1922 {
1923   int num;
1924   char c, *start;
1925   static char temp[256];
1926 
1927   start = Name;
1928   /* seek end of string */
1929   while (*Name != 0)
1930     Name++;
1931   /* back up to potential number */
1932   for (Name--; isdigit ((int) *Name); Name--);
1933   Name++;
1934   if (*Name)
1935     num = atoi (Name) + 1;
1936   else
1937     num = 1;
1938   c = *Name;
1939   *Name = 0;
1940   sprintf (temp, "%s%d", start, num);
1941   /* if this is not our string, put back the blown character */
1942   if (start != temp)
1943     *Name = c;
1944   return (temp);
1945 }
1946 
1947 /*!
1948  * \brief Make a unique name for the name on board.
1949  *
1950  * This can alter the contents of the input string.
1951  */
1952 char *
UniqueElementName(DataType * Data,char * Name)1953 UniqueElementName (DataType *Data, char *Name)
1954 {
1955   bool unique = true;
1956   /* null strings are ok */
1957   if (!Name || !*Name)
1958     return (Name);
1959 
1960   for (;;)
1961     {
1962       ELEMENT_LOOP (Data);
1963       {
1964         if (NAMEONPCB_NAME (element) &&
1965             NSTRCMP (NAMEONPCB_NAME (element), Name) == 0)
1966           {
1967             Name = BumpName (Name);
1968             unique = false;
1969             break;
1970           }
1971       }
1972       END_LOOP;
1973       if (unique)
1974         return (Name);
1975       unique = true;
1976     }
1977 }
1978 
1979 static void
GetGridLockCoordinates(int type,void * ptr1,void * ptr2,void * ptr3,Coord * x,Coord * y)1980 GetGridLockCoordinates (int type, void *ptr1,
1981                         void *ptr2, void *ptr3, Coord * x,
1982                         Coord * y)
1983 {
1984   switch (type)
1985     {
1986     case VIA_TYPE:
1987       *x = ((PinType *) ptr2)->X;
1988       *y = ((PinType *) ptr2)->Y;
1989       break;
1990     case LINE_TYPE:
1991       *x = ((LineType *) ptr2)->Point1.X;
1992       *y = ((LineType *) ptr2)->Point1.Y;
1993       break;
1994     case TEXT_TYPE:
1995     case ELEMENTNAME_TYPE:
1996       *x = ((TextType *) ptr2)->X;
1997       *y = ((TextType *) ptr2)->Y;
1998       break;
1999     case ELEMENT_TYPE:
2000       *x = ((ElementType *) ptr2)->MarkX;
2001       *y = ((ElementType *) ptr2)->MarkY;
2002       break;
2003     case POLYGON_TYPE:
2004       *x = ((PolygonType *) ptr2)->Points[0].X;
2005       *y = ((PolygonType *) ptr2)->Points[0].Y;
2006       break;
2007 
2008     case LINEPOINT_TYPE:
2009     case POLYGONPOINT_TYPE:
2010       *x = ((PointType *) ptr3)->X;
2011       *y = ((PointType *) ptr3)->Y;
2012       break;
2013     case ARC_TYPE:
2014       {
2015         BoxType *box;
2016 
2017         box = GetArcEnds ((ArcType *) ptr2);
2018         *x = box->X1;
2019         *y = box->Y1;
2020         break;
2021       }
2022     }
2023 }
2024 
2025 void
AttachForCopy(Coord PlaceX,Coord PlaceY)2026 AttachForCopy (Coord PlaceX, Coord PlaceY)
2027 {
2028   Coord mx = 0, my = 0;
2029 
2030   Crosshair.AttachedObject.RubberbandN = 0;
2031   if (! TEST_FLAG (SNAPPINFLAG, PCB))
2032     {
2033       /* dither the grab point so that the mark, center, etc
2034        * will end up on a grid coordinate
2035        */
2036       GetGridLockCoordinates (Crosshair.AttachedObject.Type,
2037                               Crosshair.AttachedObject.Ptr1,
2038                               Crosshair.AttachedObject.Ptr2,
2039                               Crosshair.AttachedObject.Ptr3, &mx, &my);
2040       mx = GridFit (mx, PCB->Grid, PCB->GridOffsetX) - mx;
2041       my = GridFit (my, PCB->Grid, PCB->GridOffsetY) - my;
2042     }
2043   Crosshair.AttachedObject.X = PlaceX - mx;
2044   Crosshair.AttachedObject.Y = PlaceY - my;
2045   if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
2046     SetLocalRef (PlaceX - mx, PlaceY - my, true);
2047   Crosshair.AttachedObject.State = STATE_SECOND;
2048 
2049   /* get boundingbox of object and set cursor range */
2050   crosshair_update_range();
2051 
2052   /* get all attached objects if necessary */
2053   if ((Settings.Mode != COPY_MODE) && TEST_FLAG (RUBBERBANDFLAG, PCB))
2054     LookupRubberbandLines (Crosshair.AttachedObject.Type,
2055                            Crosshair.AttachedObject.Ptr1,
2056                            Crosshair.AttachedObject.Ptr2,
2057                            Crosshair.AttachedObject.Ptr3);
2058   if (Settings.Mode != COPY_MODE &&
2059       (Crosshair.AttachedObject.Type == ELEMENT_TYPE ||
2060        Crosshair.AttachedObject.Type == VIA_TYPE ||
2061        Crosshair.AttachedObject.Type == LINE_TYPE ||
2062        Crosshair.AttachedObject.Type == LINEPOINT_TYPE))
2063     LookupRatLines (Crosshair.AttachedObject.Type,
2064                     Crosshair.AttachedObject.Ptr1,
2065                     Crosshair.AttachedObject.Ptr2,
2066                     Crosshair.AttachedObject.Ptr3);
2067 }
2068 
2069 /*!
2070  * \brief Return nonzero if the given file exists and is readable.
2071  */
2072 int
FileExists(const char * name)2073 FileExists (const char *name)
2074 {
2075   FILE *f;
2076   f = fopen (name, "r");
2077   if (f)
2078     {
2079       fclose (f);
2080       return 1;
2081     }
2082   return 0;
2083 }
2084 
2085 char *
Concat(const char * first,...)2086 Concat (const char *first, ...)
2087 {
2088   char *rv;
2089   int len;
2090   va_list a;
2091 
2092   len = strlen (first);
2093   rv = (char *) malloc (len + 1);
2094   strcpy (rv, first);
2095 
2096   va_start (a, first);
2097   while (1)
2098     {
2099       const char *s = va_arg (a, const char *);
2100       if (!s)
2101         break;
2102       len += strlen (s);
2103       rv = (char *) realloc (rv, len + 1);
2104       strcat (rv, s);
2105     }
2106   va_end (a);
2107   return rv;
2108 }
2109 
2110 /* Layer Group Functions. */
2111 
2112 /*!
2113  * \brief Returns group actually moved to (i.e. either group or
2114  * previous).
2115  */
2116 int
MoveLayerToGroup(int layer,int group)2117 MoveLayerToGroup (int layer, int group)
2118 {
2119   int prev, i, j;
2120 
2121   if (layer < 0 || layer > max_copper_layer + 1)
2122     return -1;
2123   prev = GetLayerGroupNumberByNumber (layer);
2124   if ((layer == bottom_silk_layer
2125         && group == GetLayerGroupNumberByNumber (top_silk_layer))
2126       || (layer == top_silk_layer
2127           && group == GetLayerGroupNumberByNumber (bottom_silk_layer))
2128       || (group < 0 || group >= max_group) || (prev == group))
2129     return prev;
2130 
2131   /* Remove layer from prev group */
2132   for (j = i = 0; i < PCB->LayerGroups.Number[prev]; i++)
2133     if (PCB->LayerGroups.Entries[prev][i] != layer)
2134       PCB->LayerGroups.Entries[prev][j++] = PCB->LayerGroups.Entries[prev][i];
2135   PCB->LayerGroups.Number[prev]--;
2136 
2137   /* Add layer to new group.  */
2138   i = PCB->LayerGroups.Number[group]++;
2139   PCB->LayerGroups.Entries[group][i] = layer;
2140 
2141   return group;
2142 }
2143 
2144 /*!
2145  * \brief Returns pointer to private buffer.
2146  */
2147 char *
LayerGroupsToString(LayerGroupType * lg)2148 LayerGroupsToString (LayerGroupType *lg)
2149 {
2150 #if MAX_ALL_LAYER < 9999
2151   /* Allows for layer numbers 0..9999 */
2152   static char buf[(MAX_ALL_LAYER) * 5 + 1];
2153 #endif
2154   char *cp = buf;
2155   char sep = 0;
2156   int group, entry;
2157   for (group = 0; group < max_group; group++)
2158     if (PCB->LayerGroups.Number[group])
2159       {
2160         if (sep)
2161           *cp++ = ':';
2162         sep = 1;
2163         for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
2164           {
2165             int layer = PCB->LayerGroups.Entries[group][entry];
2166             if (layer == top_silk_layer)
2167               {
2168                 *cp++ = 'c';
2169               }
2170             else if (layer == bottom_silk_layer)
2171               {
2172                 *cp++ = 's';
2173               }
2174             else
2175               {
2176                 sprintf (cp, "%d", layer + 1);
2177                 while (*++cp)
2178                   ;
2179               }
2180             if (entry != PCB->LayerGroups.Number[group] - 1)
2181               *cp++ = ',';
2182           }
2183       }
2184   *cp++ = 0;
2185   return buf;
2186 }
2187 
2188 int
GetMaxBottomLayer()2189 GetMaxBottomLayer ()
2190 {
2191   int l = 0;
2192   int group, i;
2193 
2194   group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
2195   for (i = 0; i < PCB->LayerGroups.Number[group]; i++)
2196     {
2197       if (PCB->LayerGroups.Entries[group][i] < max_copper_layer)
2198         l = max (l, PCB->LayerGroups.Entries[group][i]);
2199     }
2200   return l;
2201 }
2202 
2203 
2204 /*!
2205  * \brief Sanitize buried via
2206  *
2207  * - remove degraded vias
2208  * - ensure correct order of layers
2209  * - change full size vias to TH vias
2210  */
2211 void
SanitizeBuriedVia(PinType * via)2212 SanitizeBuriedVia (PinType *via)
2213 {
2214   /* we expect that via is burried/blind */
2215 
2216   if (via->BuriedFrom > via->BuriedTo)
2217     {
2218       int tmp = via->BuriedFrom;
2219       via->BuriedFrom = via->BuriedTo;
2220       via->BuriedTo = tmp;
2221     }
2222 
2223   /* check, if via was extended to full stack (after first layer removal)*/
2224   /* convert it in TH via in such case */
2225   if (via->BuriedTo == GetMaxBottomLayer ()
2226       && via->BuriedFrom == 0)
2227     via->BuriedTo = 0;
2228 }
2229 
2230 #if 0
2231 bool
2232 IsLayerMoveSafe (int old_index, int new_index)
2233 {
2234   VIA_LOOP (PCB->Data);
2235     {
2236       if (VIA_IS_BURIED (via))
2237         {
2238           /* moving from below to inside */
2239           if ((old_index < via->BuriedFrom)
2240               && (new_index < via->BuriedTo))
2241             return false;
2242 
2243           /* moving from above to inside */
2244           if ((old_index > via->BuriedTo)
2245               && (new_index > via->BuriedFrom))
2246             return false;
2247         }
2248     }
2249   END_LOOP;
2250 
2251   return true;
2252 }
2253 
2254 #endif
2255 
2256 /*!
2257  * \brief Update buried vias after layer move
2258  *
2259  */
2260 
2261 void
ChangeBuriedViasAfterLayerMove(int old_index,int new_index)2262 ChangeBuriedViasAfterLayerMove (int old_index, int new_index)
2263 {
2264   VIA_LOOP (PCB->Data);
2265     {
2266       if (VIA_IS_BURIED (via))
2267         {
2268           /* do nothing, if both layers are below the via */
2269           if ((old_index < via->BuriedFrom)
2270               && (new_index < via->BuriedFrom))
2271             continue;
2272 
2273           /* do nothing, if both layers are above the via */
2274           if ((old_index > via->BuriedTo)
2275               && (new_index > via->BuriedTo))
2276             continue;
2277 
2278           /* moving via top layer - via top follows layer */
2279           if (old_index == via->BuriedFrom)
2280             {
2281 	      AddObjectToSetViaLayersUndoList (via, via, via);
2282               via->BuriedFrom = new_index;
2283             }
2284 
2285           /* moving via bottom layer - via bottom follows layer */
2286           else if (old_index == via->BuriedTo)
2287             {
2288 	      AddObjectToSetViaLayersUndoList (via, via, via);
2289               via->BuriedTo = new_index;
2290             }
2291 
2292           /* moving layer covered by via inside the via (except top and bottom) - do nothing */
2293           else if (VIA_ON_LAYER (via, old_index)
2294               && VIA_ON_LAYER (via, new_index))
2295             continue;
2296 
2297           /* moving from inside to below - shrink via DANGEROUS op*/
2298           else if (VIA_ON_LAYER (via, old_index)
2299               && (new_index < via->BuriedFrom))
2300             {
2301 	      AddObjectToSetViaLayersUndoList (via, via, via);
2302 	      via->BuriedFrom++;
2303             }
2304 
2305           /* moving from inside to above - shrink via DANGEROUS op*/
2306           else if (VIA_ON_LAYER (via, old_index)
2307               && (new_index > via->BuriedTo))
2308             {
2309 	      AddObjectToSetViaLayersUndoList (via, via, via);
2310 	      via->BuriedTo--;
2311             }
2312 
2313           /* moving from above to below - shift via up */
2314           else if ((old_index > via->BuriedTo)
2315               && (new_index < via->BuriedFrom))
2316             {
2317 	      AddObjectToSetViaLayersUndoList (via, via, via);
2318               via->BuriedFrom++;
2319               via->BuriedTo++;
2320             }
2321 
2322           /* moving from below to above - shift via down */
2323           else if ((old_index < via->BuriedFrom)
2324               && (new_index >= via->BuriedTo))
2325             {
2326 	      AddObjectToSetViaLayersUndoList (via, via, via);
2327               via->BuriedFrom--;
2328               via->BuriedTo--;
2329             }
2330 
2331           /* moving from below to inside - extend via down DANGEROUS op*/
2332           else if ((old_index < via->BuriedFrom)
2333               && (new_index < via->BuriedTo))
2334             {
2335 	      AddObjectToSetViaLayersUndoList (via, via, via);
2336               via->BuriedFrom--;
2337             }
2338 
2339           /* moving from above to inside - extend via up DANGEROUS op*/
2340           else if ((old_index > via->BuriedTo)
2341               && (new_index > via->BuriedFrom))
2342             {
2343 	      AddObjectToSetViaLayersUndoList ( via, via, via);
2344               via->BuriedTo++;
2345             }
2346           else
2347             {
2348               Message (_("Layer move: failed via update: %d -> %d, via: %d:%d\n"), old_index, new_index, via->BuriedFrom, via->BuriedTo);
2349               continue;
2350             }
2351 
2352           SanitizeBuriedVia (via);
2353         }
2354     }
2355   END_LOOP;
2356   RemoveDegradedVias ();
2357 }
2358 
2359 /*!
2360  * \brief Update buried vias after new layer create
2361  *
2362  */
2363 void
ChangeBuriedViasAfterLayerCreate(int index)2364 ChangeBuriedViasAfterLayerCreate (int index)
2365 {
2366   VIA_LOOP (PCB->Data);
2367     {
2368       if (VIA_IS_BURIED (via))
2369         {
2370           if (index <= via->BuriedFrom
2371 	      || index <= via->BuriedTo)
2372             AddObjectToSetViaLayersUndoList (via, via, via);
2373 
2374           if (index <= via->BuriedFrom)
2375             via->BuriedFrom++;
2376           if (index <= via->BuriedTo)
2377             via->BuriedTo++;
2378         }
2379     }
2380   END_LOOP;
2381 }
2382 
2383 /*!
2384  * \brief Update buried vias after layer delete
2385  *
2386  */
2387 void
ChangeBuriedViasAfterLayerDelete(int index)2388 ChangeBuriedViasAfterLayerDelete (int index)
2389 {
2390   VIA_LOOP (PCB->Data);
2391     {
2392       if (VIA_IS_BURIED (via))
2393         {
2394           if (index < via->BuriedFrom
2395 	      || index <= via->BuriedTo)
2396             AddObjectToSetViaLayersUndoList (via, via, via);
2397 
2398           if (index < via->BuriedFrom)
2399             via->BuriedFrom--;
2400           if (index <= via->BuriedTo)
2401             via->BuriedTo--;
2402 
2403           SanitizeBuriedVia (via);
2404         }
2405     }
2406   END_LOOP;
2407   RemoveDegradedVias ();
2408 }
2409 
2410 /*!
2411  * \brief Check if via penetrates layer group
2412  *
2413  */
2414 bool
ViaIsOnLayerGroup(PinType * via,int group)2415 ViaIsOnLayerGroup (PinType *via, int group)
2416 {
2417   Cardinal layer;
2418 
2419   if (!VIA_IS_BURIED (via))
2420     return true;
2421 
2422   for (layer = via->BuriedFrom; layer <= via->BuriedTo; layer++)
2423     {
2424        if (GetLayerGroupNumberByNumber (layer) == group)
2425          return true;
2426     }
2427 
2428   return false;
2429 }
2430 
2431 /*!
2432  * \brief Check if via penetrates any visible layer
2433  *
2434  */
2435 bool
ViaIsOnAnyVisibleLayer(PinType * via)2436 ViaIsOnAnyVisibleLayer (PinType *via)
2437 {
2438   Cardinal layer;
2439 
2440   if (!VIA_IS_BURIED (via))
2441     return true;
2442 
2443   for (layer = via->BuriedFrom; layer <= via->BuriedTo; layer ++)
2444     {
2445       if (PCB->Data->Layer[layer].On)
2446         return true;
2447     }
2448 
2449   return false;
2450 }
2451 
2452 
2453 char *
pcb_author(void)2454 pcb_author (void)
2455 {
2456 #ifdef HAVE_GETPWUID
2457   static struct passwd *pwentry;
2458   static char *fab_author = 0;
2459 
2460   if (!fab_author)
2461     {
2462       if (Settings.FabAuthor && Settings.FabAuthor[0])
2463         fab_author = Settings.FabAuthor;
2464       else
2465         {
2466           int len;
2467           char *comma, *gecos;
2468 
2469           /* ID the user. */
2470           pwentry = getpwuid (getuid ());
2471           gecos = pwentry->pw_gecos;
2472           comma = strchr (gecos, ',');
2473           if (comma)
2474             len = comma - gecos;
2475           else
2476             len = strlen (gecos);
2477           fab_author = (char *)malloc (len + 1);
2478           if (!fab_author)
2479             {
2480               perror ("pcb: out of memory.\n");
2481               exit (-1);
2482             }
2483           memcpy (fab_author, gecos, len);
2484           fab_author[len] = 0;
2485         }
2486     }
2487   return fab_author;
2488 #else
2489   return "Unknown";
2490 #endif
2491 }
2492 
2493 
2494 /*!
2495  * \brief Returns NULL if the name isn't found, else the value for that
2496  * named attribute.
2497  */
2498 char *
AttributeGetFromList(AttributeListType * list,char * name)2499 AttributeGetFromList (AttributeListType *list, char *name)
2500 {
2501   int i;
2502   for (i=0; i<list->Number; i++)
2503     if (strcmp (name, list->List[i].name) == 0)
2504       return list->List[i].value;
2505   return NULL;
2506 }
2507 
2508 /*!
2509  * \brief Adds an attribute to the list.
2510  *
2511  * If the attribute already exists, whether it's replaced or a second
2512  * copy added depends on REPLACE.
2513  *
2514  * \return Non-zero if an existing attribute was replaced.
2515  */
2516 int
AttributePutToList(AttributeListType * list,const char * name,const char * value,int replace)2517 AttributePutToList (AttributeListType *list, const char *name, const char *value, int replace)
2518 {
2519   int i;
2520 
2521   /* If we're allowed to replace an existing attribute, see if we
2522      can.  */
2523   if (replace)
2524     {
2525       for (i=0; i<list->Number; i++)
2526 	if (strcmp (name, list->List[i].name) == 0)
2527 	  {
2528 	    free (list->List[i].value);
2529 	    list->List[i].value = STRDUP (value);
2530 	    return 1;
2531 	  }
2532     }
2533 
2534   /* At this point, we're going to need to add a new attribute to the
2535      list.  See if there's room.  */
2536   if (list->Number >= list->Max)
2537     {
2538       list->Max += 10;
2539       list->List = (AttributeType *) realloc (list->List,
2540 					      list->Max * sizeof (AttributeType));
2541     }
2542 
2543   /* Now add the new attribute.  */
2544   i = list->Number;
2545   list->List[i].name = STRDUP (name);
2546   list->List[i].value = STRDUP (value);
2547   list->Number ++;
2548   return 0;
2549 }
2550 
2551 /*!
2552  * \brief Remove an attribute by name.
2553  */
2554 void
AttributeRemoveFromList(AttributeListType * list,char * name)2555 AttributeRemoveFromList(AttributeListType *list, char *name)
2556 {
2557   int i, j;
2558   for (i=0; i<list->Number; i++)
2559     if (strcmp (name, list->List[i].name) == 0)
2560       {
2561 	free (list->List[i].name);
2562 	free (list->List[i].value);
2563 	for (j=i; j<list->Number-1; j++)
2564 	  list->List[j] = list->List[j+1];
2565 	list->Number --;
2566       }
2567 }
2568 
2569 /*!
2570  * \todo In future all use of this should be supplanted by pcb-printf
2571  * and %mr/%m# spec.
2572  *
2573  * These act like you'd expect, except always in the C locale.
2574  */
2575 const char *
c_dtostr(double d)2576 c_dtostr (double d)
2577 {
2578   static char buf[100];
2579   int i, f;
2580   char *bufp = buf;
2581 
2582   if (d < 0)
2583     {
2584       *bufp++ = '-';
2585       d = -d;
2586     }
2587   d += 0.0000005;               /* rounding */
2588   i = floor (d);
2589   d -= i;
2590   sprintf (bufp, "%d", i);
2591   bufp += strlen (bufp);
2592   *bufp++ = '.';
2593 
2594   f = floor (d * 1000000.0);
2595   sprintf (bufp, "%06d", f);
2596   return buf;
2597 }
2598 
2599 void
r_delete_element(DataType * data,ElementType * element)2600 r_delete_element (DataType * data, ElementType * element)
2601 {
2602   r_delete_entry (data->element_tree, (BoxType *) element);
2603   PIN_LOOP (element);
2604   {
2605     r_delete_entry (data->pin_tree, (BoxType *) pin);
2606   }
2607   END_LOOP;
2608   PAD_LOOP (element);
2609   {
2610     r_delete_entry (data->pad_tree, (BoxType *) pad);
2611   }
2612   END_LOOP;
2613   ELEMENTTEXT_LOOP (element);
2614   {
2615     r_delete_entry (data->name_tree[n], (BoxType *) text);
2616   }
2617   END_LOOP;
2618 }
2619 
2620 
2621 /*!
2622  * \brief Returns a string that has a bunch of information about the
2623  * program.
2624  *
2625  * Can be used for things like "about" dialog boxes.
2626  */
2627 char *
GetInfoString(void)2628 GetInfoString (void)
2629 {
2630   HID **hids;
2631   int i;
2632   static DynamicStringType info;
2633   static int first_time = 1;
2634 
2635 #define TAB "    "
2636 
2637   if (first_time)
2638     {
2639       first_time = 0;
2640       DSAddString (&info,
2641 	  _("This is PCB, an interactive\n"
2642 	    "printed circuit board editor\n"
2643 	    "version "));
2644       DSAddString (&info,
2645 	    VERSION "\n\n"
2646 	    "Compiled on " __DATE__ " at " __TIME__ "\n\n"
2647 	    "by harry eaton\n\n"
2648 	    "Copyright (C) Thomas Nau 1994, 1995, 1996, 1997\n"
2649 	    "Copyright (C) harry eaton 1998-2007\n"
2650 	    "Copyright (C) C. Scott Ananian 2001\n"
2651 	    "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n"
2652 	    "Copyright (C) Dan McMahill 2003, 2004, 2005, 2006, 2007, 2008\n\n");
2653       DSAddString (&info,
2654 	  _("It is licensed under the terms of the GNU\n"
2655 	    "General Public License version 2\n"
2656 	    "See the LICENSE file for more information\n\n"
2657 	    "For more information see:\n"));
2658       DSAddString (&info, _("PCB homepage: "));
2659       DSAddString (&info, "http://pcb.geda-project.org\n");
2660       DSAddString (&info, _("gEDA homepage: "));
2661       DSAddString (&info, "http://www.geda-project.org\n");
2662       DSAddString (&info, _("gEDA Wiki: "));
2663       DSAddString (&info, "http://wiki.geda-project.org\n");
2664 
2665       DSAddString (&info, _("\n----- Compile Time Options -----\n"));
2666       hids = hid_enumerate ();
2667       DSAddString (&info, _("GUI:\n"));
2668       for (i = 0; hids[i]; i++)
2669         {
2670           if (hids[i]->gui)
2671             {
2672               DSAddString (&info, TAB);
2673               DSAddString (&info, hids[i]->name);
2674               DSAddString (&info, " : ");
2675               DSAddString (&info, hids[i]->description);
2676               DSAddString (&info, "\n");
2677             }
2678         }
2679 
2680       DSAddString (&info, _("Exporters:\n"));
2681       for (i = 0; hids[i]; i++)
2682         {
2683           if (hids[i]->exporter)
2684             {
2685               DSAddString (&info, TAB);
2686               DSAddString (&info, hids[i]->name);
2687               DSAddString (&info, " : ");
2688               DSAddString (&info, hids[i]->description);
2689               DSAddString (&info, "\n");
2690             }
2691         }
2692 
2693       DSAddString (&info, _("Printers:\n"));
2694       for (i = 0; hids[i]; i++)
2695         {
2696           if (hids[i]->printer)
2697             {
2698               DSAddString (&info, TAB);
2699               DSAddString (&info, hids[i]->name);
2700               DSAddString (&info, " : ");
2701               DSAddString (&info, hids[i]->description);
2702               DSAddString (&info, "\n");
2703             }
2704         }
2705     }
2706 #undef TAB
2707 
2708   return info.Data;
2709 }
2710 
2711 #ifdef MKDIR_IS_PCBMKDIR
2712 #error "Don't know how to create a directory on this system."
2713 #endif
2714 
2715 /*!
2716  * \brief mkdir() implentation, mostly for plugins, which don't have our
2717  * config.h.
2718  */
2719 int
pcb_mkdir(const char * path,int mode)2720 pcb_mkdir (const char *path, int mode)
2721 {
2722   return MKDIR (path, mode);
2723 }
2724 
2725 /*!
2726  * \brief Returns a best guess about the orientation of an element.
2727  *
2728  * The value corresponds to the rotation; a difference is the right
2729  * value to pass to RotateElementLowLevel.
2730  * However, the actual value is no indication of absolute rotation; only
2731  * relative rotation is meaningful.
2732  *
2733  * \return a relative rotation for an element, useful only for comparing
2734  * two similar footprints.
2735  */
2736 int
ElementOrientation(ElementType * e)2737 ElementOrientation (ElementType *e)
2738 {
2739   Coord pin1x, pin1y, pin2x, pin2y, dx, dy;
2740   bool found_pin1 = 0;
2741   bool found_pin2 = 0;
2742 
2743   /* in case we don't find pin 1 or 2, make sure we have initialized these variables */
2744   pin1x = 0;
2745   pin1y = 0;
2746   pin2x = 0;
2747   pin2y = 0;
2748 
2749   PIN_LOOP (e);
2750   {
2751     if (NSTRCMP (pin->Number, "1") == 0)
2752       {
2753 	pin1x = pin->X;
2754 	pin1y = pin->Y;
2755 	found_pin1 = 1;
2756       }
2757     else if (NSTRCMP (pin->Number, "2") == 0)
2758       {
2759 	pin2x = pin->X;
2760 	pin2y = pin->Y;
2761 	found_pin2 = 1;
2762       }
2763   }
2764   END_LOOP;
2765 
2766   PAD_LOOP (e);
2767   {
2768     if (NSTRCMP (pad->Number, "1") == 0)
2769       {
2770 	pin1x = (pad->Point1.X + pad->Point2.X) / 2;
2771 	pin1y = (pad->Point1.Y + pad->Point2.Y) / 2;
2772 	found_pin1 = 1;
2773       }
2774     else if (NSTRCMP (pad->Number, "2") == 0)
2775       {
2776 	pin2x = (pad->Point1.X + pad->Point2.X) / 2;
2777 	pin2y = (pad->Point1.Y + pad->Point2.Y) / 2;
2778 	found_pin2 = 1;
2779       }
2780   }
2781   END_LOOP;
2782 
2783   if (found_pin1 && found_pin2)
2784     {
2785       dx = pin2x - pin1x;
2786       dy = pin2y - pin1y;
2787     }
2788   else if (found_pin1 && (pin1x || pin1y))
2789     {
2790       dx = pin1x;
2791       dy = pin1y;
2792     }
2793   else if (found_pin2 && (pin2x || pin2y))
2794     {
2795       dx = pin2x;
2796       dy = pin2y;
2797     }
2798   else
2799     return 0;
2800 
2801   if (abs(dx) > abs(dy))
2802     return dx > 0 ? 0 : 2;
2803   return dy > 0 ? 3 : 1;
2804 }
2805 
2806 int
ActionListRotations(int argc,char ** argv,Coord x,Coord y)2807 ActionListRotations(int argc, char **argv, Coord x, Coord y)
2808 {
2809   ELEMENT_LOOP (PCB->Data);
2810   {
2811     printf("%d %s\n", ElementOrientation(element), NAMEONPCB_NAME(element));
2812   }
2813   END_LOOP;
2814 
2815   return 0;
2816 }
2817 
2818 HID_Action misc_action_list[] = {
2819   {"ListRotations", 0, ActionListRotations,
2820    0,0},
2821 };
2822 
2823 REGISTER_ACTIONS (misc_action_list)
2824