6 * PCB, interactive printed circuit board design
7 * Copyright (C) 1994,1995,1996,1997,1998,2005,2006 Thomas Nau
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 * Contact addresses for paper mail and Email:
24 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
25 * Thomas.Nau@rz.uni-ulm.de
29 /* file save, load, merge ... routines
30 * getpid() needs a cast to (int) to get rid of compiler warnings
31 * on several architectures
38 #ifdef HAVE_SYS_PARAM_H
39 #include <sys/param.h>
50 #ifdef HAVE_SYS_SOCKET_H
51 #include <sys/socket.h>
56 #ifdef HAVE_NETINET_IN_H
57 #include <netinet/in.h>
82 #include "crosshair.h"
84 #include "edif_parse.h"
98 #ifdef HAVE_LIBDMALLOC
104 #if !defined(HAS_ATEXIT) && !defined(HAS_ON_EXIT)
105 /* ---------------------------------------------------------------------------
106 * some local identifiers for OS without an atexit() or on_exit()
109 static char TMPFilename
[80];
112 /* ---------------------------------------------------------------------------
113 * some local prototypes
115 static void PrintQuotedString (FILE *, char *);
116 static void WritePCBInfoHeader (FILE *);
117 static void WritePCBDataHeader (FILE *);
118 static void WritePCBFontData (FILE *);
119 static void WriteViaData (FILE *, DataTypePtr
);
120 static void WritePCBRatData (FILE *);
121 static void WriteElementData (FILE *, DataTypePtr
);
122 static void WriteLayerData (FILE *, Cardinal
, LayerTypePtr
);
123 static int WritePCB (FILE *);
124 static int WritePCBFile (char *);
125 static int WritePipe (char *, bool);
126 static int ParseLibraryTree (void);
127 static int LoadNewlibFootprintsFromDir(char *path
, char *toppath
);
128 static char *pcb_basename (char *p
);
130 /* ---------------------------------------------------------------------------
131 * Flag helper functions
134 #define F2S(OBJ, TYPE) flags_to_string ((OBJ)->Flags, TYPE)
136 /* --------------------------------------------------------------------------- */
139 string_cmp (const char *a
, const char *b
)
143 if (isdigit ((int) *a
) && isdigit ((int) *b
))
149 while (isdigit ((int) *a
) && *(a
+1))
151 while (isdigit ((int) *b
) && *(b
+1))
154 else if (tolower ((int) *a
) != tolower ((int) *b
))
155 return tolower ((int) *a
) - tolower ((int) *b
);
166 static int netlist_sort_offset
= 0;
169 netlist_sort (const void *va
, const void *vb
)
171 LibraryMenuType
*am
= (LibraryMenuType
*) va
;
172 LibraryMenuType
*bm
= (LibraryMenuType
*) vb
;
179 return string_cmp (a
, b
);
183 netnode_sort (const void *va
, const void *vb
)
185 LibraryEntryType
*am
= (LibraryEntryType
*) va
;
186 LibraryEntryType
*bm
= (LibraryEntryType
*) vb
;
187 char *a
= am
->ListEntry
;
188 char *b
= bm
->ListEntry
;
189 return string_cmp (a
, b
);
193 sort_library (LibraryTypePtr lib
)
196 qsort (lib
->Menu
, lib
->MenuN
, sizeof (lib
->Menu
[0]), netlist_sort
);
197 for (i
= 0; i
< lib
->MenuN
; i
++)
198 qsort (lib
->Menu
[i
].Entry
,
199 lib
->Menu
[i
].EntryN
, sizeof (lib
->Menu
[i
].Entry
[0]), netnode_sort
);
205 netlist_sort_offset
= 2;
206 sort_library (&(PCB
->NetlistLib
));
207 netlist_sort_offset
= 0;
210 /* ---------------------------------------------------------------------------
211 * opens a file and check if it exists
214 CheckAndOpenFile (char *Filename
, bool Confirm
, bool AllButton
,
215 bool * WasAllButton
, bool * WasCancelButton
)
219 char message
[MAXPATHLEN
+ 80];
222 if (Filename
&& *Filename
)
224 if (!stat (Filename
, &buffer
) && Confirm
)
226 sprintf (message
, _("File '%s' exists, use anyway?"), Filename
);
228 *WasAllButton
= false;
230 *WasCancelButton
= false;
233 gui
->confirm_dialog (message
, "Cancel", "Ok",
234 AllButton
? "Sequence OK" : 0);
237 gui
->confirm_dialog (message
, "Cancel", "Ok", "Sequence OK");
243 *WasAllButton
= true;
247 *WasCancelButton
= true;
250 if ((fp
= fopen (Filename
, "w")) == NULL
)
251 OpenErrorMessage (Filename
);
256 /* ---------------------------------------------------------------------------
257 * opens a file for saving connection data
260 OpenConnectionDataFile (void)
264 static char * default_file
= NULL
;
265 bool result
; /* not used */
267 /* CheckAndOpenFile deals with the case where fname already exists */
268 fname
= gui
->fileselect (_("Save Connection Data As ..."),
269 _("Choose a file to save all connection data to."),
270 default_file
, ".net", "connection_data",
275 if (default_file
!= NULL
)
282 default_file
= strdup (fname
);
284 fp
= CheckAndOpenFile (fname
, true, false, &result
, NULL
);
290 /* ---------------------------------------------------------------------------
291 * save elements in the current buffer
294 SaveBufferElements (char *Filename
)
300 result
= WritePipe (Filename
, false);
306 /* ---------------------------------------------------------------------------
310 SavePCB (char *Filename
)
315 if (!(retcode
= WritePipe (Filename
, true)))
317 /* thanks to Nick Bailey for the bug-fix;
318 * first of all make a copy of the passed filename because
319 * it might be identical to 'PCB->Filename'
321 copy
= strdup (Filename
);
322 free (PCB
->Filename
);
323 PCB
->Filename
= copy
;
324 SetChangedFlag (false);
329 /* ---------------------------------------------------------------------------
330 * set the route style to the first one, if the current one doesn't
331 * happen to match any. This way, "revert" won't change the route
335 set_some_route_style ()
337 if (hid_get_flag ("style"))
339 SetLineSize (PCB
->RouteStyle
[0].Thick
);
340 SetViaSize (PCB
->RouteStyle
[0].Diameter
, true);
341 SetViaDrillingHole (PCB
->RouteStyle
[0].Hole
, true);
342 SetKeepawayWidth (PCB
->RouteStyle
[0].Keepaway
);
345 /* ---------------------------------------------------------------------------
347 * parse the file with enabled 'PCB mode' (see parser)
348 * if successful, update some other stuff
351 LoadPCB (char *Filename
)
353 PCBTypePtr newPCB
= CreateNewPCB (false);
362 /* new data isn't added to the undo list */
363 if (!ParsePCB (newPCB
, Filename
))
368 CreateNewPCBPost (PCB
, 0);
369 ResetStackAndVisibility ();
371 /* update cursor location */
372 Crosshair
.X
= MAX (0, MIN (PCB
->CursorX
, (LocationType
) PCB
->MaxWidth
));
374 MAX (0, MIN (PCB
->CursorY
, (LocationType
) PCB
->MaxHeight
));
376 Xorig
= Crosshair
.X
- TO_PCB (Output
.Width
/ 2);
377 Yorig
= Crosshair
.Y
- TO_PCB (Output
.Height
/ 2);
379 /* update cursor confinement and output area (scrollbars) */
380 ChangePCBSize (PCB
->MaxWidth
, PCB
->MaxHeight
);
382 /* create default font if necessary */
383 if (!PCB
->Font
.Valid
)
386 ("File '%s' has no font information, using default font\n"),
388 CreateDefaultFont ();
391 /* clear 'changed flag' */
392 SetChangedFlag (false);
393 PCB
->Filename
= strdup (Filename
);
394 /* just in case a bad file saved file is loaded */
396 units_mm
= (PCB
->Grid
!= (int) PCB
->Grid
) ? true : false;
398 Settings
.grid_units_mm
= units_mm
;
402 set_some_route_style ();
404 hid_action ("PCBChanged");
408 elapsed
= ((double) (end
- start
)) / CLOCKS_PER_SEC
;
409 gui
->log ("Loading file %s took %f seconds of CPU time\n",
415 hid_action ("PCBChanged");
417 /* release unused memory */
422 /* ---------------------------------------------------------------------------
423 * functions for loading elements-as-pcb
426 extern PCBTypePtr yyPCB
;
427 extern DataTypePtr yyData
;
428 extern FontTypePtr yyFont
;
437 yyFont
= &yyPCB
->Font
;
438 yyData
= yyPCB
->Data
;
439 yyData
->pcb
= (void *)yyPCB
;
444 PostLoadElementPCB ()
446 PCBTypePtr pcb_save
= PCB
;
452 CreateNewPCBPost (yyPCB
, 0);
453 ParseGroupString("1,c:2,s", &yyPCB
->LayerGroups
, yyData
->LayerN
);
454 e
= yyPCB
->Data
->Element
; /* we know there's only one */
456 MoveElementLowLevel (yyPCB
->Data
,
457 e
, -e
->BoundingBox
.X1
, -e
->BoundingBox
.Y1
);
459 yyPCB
->MaxWidth
= e
->BoundingBox
.X2
;
460 yyPCB
->MaxHeight
= e
->BoundingBox
.Y2
;
463 /* ---------------------------------------------------------------------------
464 * writes the quoted string created by another subroutine
467 PrintQuotedString (FILE * FP
, char *S
)
469 static DynamicStringType ds
;
471 CreateQuotedString (&ds
, S
);
475 /* ---------------------------------------------------------------------------
476 * writes out an attribute list
479 WriteAttributeList (FILE * FP
, AttributeListTypePtr list
, char *prefix
)
483 for (i
= 0; i
< list
->Number
; i
++)
484 fprintf (FP
, "%sAttribute(\"%s\" \"%s\")\n",
485 prefix
, list
->List
[i
].name
, list
->List
[i
].value
);
488 /* ---------------------------------------------------------------------------
489 * writes layout header information
490 * date, UID and name of user
493 WritePCBInfoHeader (FILE * FP
)
496 struct passwd
*pwentry
;
499 #ifdef HAVE_GETHOSTNAME
500 static struct hostent
*hostentry
= NULL
;
505 /* write some useful comments */
506 currenttime
= time (NULL
);
507 fprintf (FP
, "# release: %s " VERSION
"\n", Progname
);
508 fprintf (FP
, "# date: %s", asctime (localtime (¤ttime
)));
511 pwentry
= getpwuid (getuid ());
512 fprintf (FP
, "# user: %s (%s)\n", pwentry
->pw_name
, pwentry
->pw_gecos
);
514 fprintf (FP
, "# user: Unknown\n");
517 #ifdef HAVE_GETHOSTNAME
518 if (gethostname (hostname
, 255) != -1)
520 if (hostentry
== NULL
)
521 hostentry
= gethostbyname (hostname
);
522 fprintf (FP
, "# host: %s\n",
523 hostentry
? hostentry
->h_name
: hostname
);
526 fprintf (FP
, "# host: Unknown\n");
530 /* ---------------------------------------------------------------------------
532 * the name of the PCB, cursor location, zoom and grid
533 * layergroups and some flags
536 WritePCBDataHeader (FILE * FP
)
541 * ************************** README *******************
542 * ************************** README *******************
544 * If the file format is modified in any way, update
545 * PCB_FILE_VERSION in file.h
547 * ************************** README *******************
548 * ************************** README *******************
551 fprintf (FP
, "\n# To read pcb files, the pcb version (or the git source date) must be >= the file version\n");
552 fprintf (FP
, "FileVersion[%i]\n", PCB_FILE_VERSION
);
554 fputs ("\nPCB[", FP
);
555 PrintQuotedString (FP
, EMPTY (PCB
->Name
));
556 fprintf (FP
, " %i %i]\n\n", (int) PCB
->MaxWidth
, (int) PCB
->MaxHeight
);
557 fprintf (FP
, "Grid[%s %i %i %i]\n",
558 c_dtostr (PCB
->Grid
), (int) PCB
->GridOffsetX
,
559 (int) PCB
->GridOffsetY
, (int) Settings
.DrawGrid
);
560 fprintf (FP
, "Cursor[%i %i %s]\n", (int) TO_PCB_X (Output
.Width
/ 2),
561 (int) TO_PCB_Y (Output
.Height
/ 2), c_dtostr (PCB
->Zoom
));
562 fprintf (FP
, "PolyArea[%s]\n", c_dtostr (PCB
->IsleArea
));
563 fprintf (FP
, "Thermal[%s]\n", c_dtostr (PCB
->ThermScale
));
564 fprintf (FP
, "DRC[%i %i %i %i %i %i]\n", PCB
->Bloat
, PCB
->Shrink
,
565 PCB
->minWid
, PCB
->minSlk
, PCB
->minDrill
, PCB
->minRing
);
566 fprintf (FP
, "Flags(%s)\n", pcbflags_to_string(PCB
->Flags
));
567 fprintf (FP
, "Groups(\"%s\")\n", LayerGroupsToString (&PCB
->LayerGroups
));
568 fputs ("Styles[\"", FP
);
569 for (group
= 0; group
< NUM_STYLES
- 1; group
++)
570 fprintf (FP
, "%s,%i,%i,%i,%i:", PCB
->RouteStyle
[group
].Name
,
571 PCB
->RouteStyle
[group
].Thick
,
572 PCB
->RouteStyle
[group
].Diameter
,
573 PCB
->RouteStyle
[group
].Hole
, PCB
->RouteStyle
[group
].Keepaway
);
574 fprintf (FP
, "%s,%i,%i,%i,%i\"]\n\n", PCB
->RouteStyle
[group
].Name
,
575 PCB
->RouteStyle
[group
].Thick
,
576 PCB
->RouteStyle
[group
].Diameter
,
577 PCB
->RouteStyle
[group
].Hole
, PCB
->RouteStyle
[group
].Keepaway
);
580 /* ---------------------------------------------------------------------------
581 * writes font data of non empty symbols
584 WritePCBFontData (FILE * FP
)
590 for (font
= &PCB
->Font
, i
= 0; i
<= MAX_FONTPOSITION
; i
++)
592 if (!font
->Symbol
[i
].Valid
)
595 if (isprint (i
) && font
->Symbol
[i
].Delta
% 100 == 0)
596 fprintf (FP
, "Symbol('%c' %i)\n(\n",
597 (char) i
, (int) font
->Symbol
[i
].Delta
/ 100);
598 else if (isprint (i
))
599 fprintf (FP
, "Symbol['%c' %i]\n(\n",
600 (char) i
, (int) font
->Symbol
[i
].Delta
);
602 fprintf (FP
, "Symbol[%i %i]\n(\n", i
, (int) font
->Symbol
[i
].Delta
);
604 line
= font
->Symbol
[i
].Line
;
605 for (j
= font
->Symbol
[i
].LineN
; j
; j
--, line
++)
607 if (line
->Point1
.X
% 100 == 0
608 && line
->Point1
.Y
% 100 == 0
609 && line
->Point2
.X
% 100 == 0
610 && line
->Point2
.Y
% 100 == 0 && line
->Thickness
% 100 == 0)
611 fprintf (FP
, "\tSymbolLine(%i %i %i %i %i)\n",
612 line
->Point1
.X
/ 100, line
->Point1
.Y
/ 100,
613 line
->Point2
.X
/ 100, line
->Point2
.Y
/ 100,
614 line
->Thickness
/ 100);
616 fprintf (FP
, "\tSymbolLine[%i %i %i %i %i]\n",
617 line
->Point1
.X
, line
->Point1
.Y
,
618 line
->Point2
.X
, line
->Point2
.Y
, line
->Thickness
);
624 /* ---------------------------------------------------------------------------
628 WriteViaData (FILE * FP
, DataTypePtr Data
)
631 /* write information about vias */
632 for (n
= 0; n
< Data
->ViaN
; n
++)
634 PinTypePtr via
= &Data
->Via
[n
];
635 fprintf (FP
, "Via[%i %i %i %i %i %i ",
637 via
->Thickness
, via
->Clearance
, via
->Mask
, via
->DrillingHole
);
638 PrintQuotedString (FP
, EMPTY (via
->Name
));
639 fprintf (FP
, " %s]\n", F2S (via
, VIA_TYPE
));
643 /* ---------------------------------------------------------------------------
644 * writes rat-line data
647 WritePCBRatData (FILE * FP
)
650 /* write information about rats */
651 for (n
= 0; n
< PCB
->Data
->RatN
; n
++)
653 RatTypePtr line
= &PCB
->Data
->Rat
[n
];
654 fprintf (FP
, "Rat[%i %i %i %i %i %i ",
655 (int) line
->Point1
.X
, (int) line
->Point1
.Y
,
656 (int) line
->group1
, (int) line
->Point2
.X
,
657 (int) line
->Point2
.Y
, (int) line
->group2
);
658 fprintf (FP
, " %s]\n", F2S (line
, RATLINE_TYPE
));
662 /* ---------------------------------------------------------------------------
663 * writes netlist data
666 WritePCBNetlistData (FILE * FP
)
668 /* write out the netlist if it exists */
669 if (PCB
->NetlistLib
.MenuN
)
672 fprintf (FP
, "NetList()\n(\n");
674 for (n
= 0; n
< PCB
->NetlistLib
.MenuN
; n
++)
676 LibraryMenuTypePtr menu
= &PCB
->NetlistLib
.Menu
[n
];
677 fprintf (FP
, "\tNet(");
678 PrintQuotedString(FP
, &menu
->Name
[2]);
680 PrintQuotedString(FP
, UNKNOWN (menu
->Style
));
681 fprintf (FP
, ")\n\t(\n");
682 for (p
= 0; p
< menu
->EntryN
; p
++)
684 LibraryEntryTypePtr entry
= &menu
->Entry
[p
];
685 fprintf (FP
, "\t\tConnect(");
686 PrintQuotedString (FP
, entry
->ListEntry
);
689 fprintf (FP
, "\t)\n");
695 /* ---------------------------------------------------------------------------
696 * writes element data
699 WriteElementData (FILE * FP
, DataTypePtr Data
)
702 for (n
= 0; n
< Data
->ElementN
; n
++)
704 ElementTypePtr element
= &Data
->Element
[n
];
705 /* only non empty elements */
706 if (!element
->LineN
&& !element
->PinN
&& !element
->ArcN
709 /* the coordinates and text-flags are the same for
710 * both names of an element
712 fprintf (FP
, "\nElement[%s ", F2S (element
, ELEMENT_TYPE
));
713 PrintQuotedString (FP
, EMPTY (DESCRIPTION_NAME (element
)));
715 PrintQuotedString (FP
, EMPTY (NAMEONPCB_NAME (element
)));
717 PrintQuotedString (FP
, EMPTY (VALUE_NAME (element
)));
718 fprintf (FP
, " %i %i %i %i %i %i %s]\n(\n",
719 (int) element
->MarkX
, (int) element
->MarkY
,
720 (int) (DESCRIPTION_TEXT (element
).X
-
722 (int) (DESCRIPTION_TEXT (element
).Y
-
724 (int) DESCRIPTION_TEXT (element
).Direction
,
725 (int) DESCRIPTION_TEXT (element
).Scale
,
726 F2S (&(DESCRIPTION_TEXT (element
)), ELEMENTNAME_TYPE
));
727 WriteAttributeList (FP
, &element
->Attributes
, "\t");
728 for (p
= 0; p
< element
->PinN
; p
++)
730 PinTypePtr pin
= &element
->Pin
[p
];
731 fprintf (FP
, "\tPin[%i %i %i %i %i %i ",
732 (int) (pin
->X
- element
->MarkX
),
733 (int) (pin
->Y
- element
->MarkY
),
734 (int) pin
->Thickness
, (int) pin
->Clearance
,
735 (int) pin
->Mask
, (int) pin
->DrillingHole
);
736 PrintQuotedString (FP
, EMPTY (pin
->Name
));
738 PrintQuotedString (FP
, EMPTY (pin
->Number
));
739 fprintf (FP
, " %s]\n", F2S (pin
, PIN_TYPE
));
741 for (p
= 0; p
< element
->PadN
; p
++)
743 PadTypePtr pad
= &element
->Pad
[p
];
744 fprintf (FP
, "\tPad[%i %i %i %i %i %i %i ",
745 (int) (pad
->Point1
.X
- element
->MarkX
),
746 (int) (pad
->Point1
.Y
- element
->MarkY
),
747 (int) (pad
->Point2
.X
- element
->MarkX
),
748 (int) (pad
->Point2
.Y
- element
->MarkY
),
749 (int) pad
->Thickness
, (int) pad
->Clearance
,
751 PrintQuotedString (FP
, EMPTY (pad
->Name
));
753 PrintQuotedString (FP
, EMPTY (pad
->Number
));
754 fprintf (FP
, " %s]\n", F2S (pad
, PAD_TYPE
));
756 for (p
= 0; p
< element
->LineN
; p
++)
758 LineTypePtr line
= &element
->Line
[p
];
760 "\tElementLine [%i %i %i %i %i]\n",
761 (int) (line
->Point1
.X
-
763 (int) (line
->Point1
.Y
-
765 (int) (line
->Point2
.X
-
767 (int) (line
->Point2
.Y
-
768 element
->MarkY
), (int) line
->Thickness
);
770 for (p
= 0; p
< element
->ArcN
; p
++)
772 ArcTypePtr arc
= &element
->Arc
[p
];
774 "\tElementArc [%i %i %i %i %i %i %i]\n",
775 (int) (arc
->X
- element
->MarkX
),
776 (int) (arc
->Y
- element
->MarkY
),
777 (int) arc
->Width
, (int) arc
->Height
,
778 (int) arc
->StartAngle
, (int) arc
->Delta
,
779 (int) arc
->Thickness
);
781 fputs ("\n\t)\n", FP
);
785 /* ---------------------------------------------------------------------------
789 WriteLayerData (FILE * FP
, Cardinal Number
, LayerTypePtr layer
)
792 /* write information about non empty layers */
793 if (layer
->LineN
|| layer
->ArcN
|| layer
->TextN
|| layer
->PolygonN
||
794 (layer
->Name
&& *layer
->Name
))
796 fprintf (FP
, "Layer(%i ", (int) Number
+ 1);
797 PrintQuotedString (FP
, EMPTY (layer
->Name
));
798 fputs (")\n(\n", FP
);
799 WriteAttributeList (FP
, &layer
->Attributes
, "\t");
801 for (n
= 0; n
< layer
->LineN
; n
++)
803 LineTypePtr line
= &layer
->Line
[n
];
804 fprintf (FP
, "\tLine[%i %i %i %i %i %i %s]\n",
805 (int) line
->Point1
.X
, (int) line
->Point1
.Y
,
806 (int) line
->Point2
.X
, (int) line
->Point2
.Y
,
807 (int) line
->Thickness
, (int) line
->Clearance
,
808 F2S (line
, LINE_TYPE
));
810 for (n
= 0; n
< layer
->ArcN
; n
++)
812 ArcTypePtr arc
= &layer
->Arc
[n
];
813 fprintf (FP
, "\tArc[%i %i %i %i %i %i %i %i %s]\n",
814 (int) arc
->X
, (int) arc
->Y
, (int) arc
->Width
,
815 (int) arc
->Height
, (int) arc
->Thickness
,
816 (int) arc
->Clearance
, (int) arc
->StartAngle
,
817 (int) arc
->Delta
, F2S (arc
, ARC_TYPE
));
819 for (n
= 0; n
< layer
->TextN
; n
++)
821 TextTypePtr text
= &layer
->Text
[n
];
822 fprintf (FP
, "\tText[%i %i %i %i ",
823 (int) text
->X
, (int) text
->Y
,
824 (int) text
->Direction
, (int) text
->Scale
);
825 PrintQuotedString (FP
, EMPTY (text
->TextString
));
826 fprintf (FP
, " %s]\n", F2S (text
, TEXT_TYPE
));
828 for (n
= 0; n
< layer
->PolygonN
; n
++)
830 PolygonTypePtr polygon
= &layer
->Polygon
[n
];
833 fprintf (FP
, "\tPolygon(%s)\n\t(", F2S (polygon
, POLYGON_TYPE
));
834 for (p
= 0; p
< polygon
->PointN
; p
++)
836 PointTypePtr point
= &polygon
->Points
[p
];
838 if (hole
< polygon
->HoleIndexN
&&
839 p
== polygon
->HoleIndex
[hole
])
842 fputs ("\n\t\t)", FP
);
843 fputs ("\n\t\tHole (", FP
);
850 fputs ("\n\t\t", FP
);
854 fprintf (FP
, "[%i %i] ", (int) point
->X
, (int) point
->Y
);
857 fputs ("\n\t\t)", FP
);
858 fputs ("\n\t)\n", FP
);
864 /* ---------------------------------------------------------------------------
865 * writes just the elements in the buffer to file
868 WriteBuffer (FILE * FP
)
872 WriteViaData (FP
, PASTEBUFFER
->Data
);
873 WriteElementData (FP
, PASTEBUFFER
->Data
);
874 for (i
= 0; i
< max_copper_layer
+ 2; i
++)
875 WriteLayerData (FP
, i
, &(PASTEBUFFER
->Data
->Layer
[i
]));
879 /* ---------------------------------------------------------------------------
887 WritePCBInfoHeader (FP
);
888 WritePCBDataHeader (FP
);
889 WritePCBFontData (FP
);
890 WriteAttributeList (FP
, &PCB
->Attributes
, "");
891 WriteViaData (FP
, PCB
->Data
);
892 WriteElementData (FP
, PCB
->Data
);
893 WritePCBRatData (FP
);
894 for (i
= 0; i
< max_copper_layer
+ 2; i
++)
895 WriteLayerData (FP
, i
, &(PCB
->Data
->Layer
[i
]));
896 WritePCBNetlistData (FP
);
901 /* ---------------------------------------------------------------------------
905 WritePCBFile (char *Filename
)
910 if ((fp
= fopen (Filename
, "w")) == NULL
)
912 OpenErrorMessage (Filename
);
913 return (STATUS_ERROR
);
915 result
= WritePCB (fp
);
920 /* ---------------------------------------------------------------------------
921 * writes to pipe using the command defined by Settings.SaveCommand
922 * %f are replaced by the passed filename
925 WritePipe (char *Filename
, bool thePcb
)
930 static DynamicStringType command
;
933 if (EMPTY_STRING_P (Settings
.SaveCommand
))
935 fp
= fopen (Filename
, "w");
938 Message ("Unable to write to file %s\n", Filename
);
945 /* setup commandline */
946 DSClearString (&command
);
947 for (p
= Settings
.SaveCommand
; *p
; p
++)
949 /* copy character if not special or add string to command */
950 if (!(*p
== '%' && *(p
+ 1) == 'f'))
951 DSAddCharacter (&command
, *p
);
954 DSAddString (&command
, Filename
);
956 /* skip the character */
960 DSAddCharacter (&command
, '\0');
961 printf ("write to pipe \"%s\"\n", command
.Data
);
962 if ((fp
= popen (command
.Data
, "w")) == NULL
)
964 PopenErrorMessage (command
.Data
);
965 return (STATUS_ERROR
);
969 result
= WritePCB (fp
);
971 result
= WriteBuffer (fp
);
974 return (pclose (fp
) ? STATUS_ERROR
: result
);
975 return (fclose (fp
) ? STATUS_ERROR
: result
);
978 /* ---------------------------------------------------------------------------
979 * saves the layout in a temporary file
980 * this is used for fatal errors and does not call the program specified
981 * in 'saveCommand' for safety reasons
988 /* memory might have been released before this function is called */
989 if (PCB
&& PCB
->Changed
)
991 sprintf (filename
, EMERGENCY_NAME
, (int) getpid ());
992 Message (_("Trying to save your layout in '%s'\n"), filename
);
993 WritePCBFile (filename
);
997 /* ---------------------------------------------------------------------------
998 * front-end for 'SaveInTMP()'
999 * just makes sure that the routine is only called once
1001 static bool dont_save_any_more
= false;
1003 EmergencySave (void)
1006 if (!dont_save_any_more
)
1009 dont_save_any_more
= true;
1014 DisableEmergencySave (void)
1016 dont_save_any_more
= true;
1019 /* ----------------------------------------------------------------------
1020 * Callback for the autosave
1023 static hidval backup_timer
;
1026 * If the backup interval is > 0 then set another timer. Otherwise
1027 * we do nothing and it is up to the GUI to call EnableAutosave()
1028 * after setting Settings.BackupInterval > 0 again.
1031 backup_cb (hidval data
)
1033 backup_timer
.ptr
= NULL
;
1035 if (Settings
.BackupInterval
> 0 && gui
->add_timer
)
1036 backup_timer
= gui
->add_timer (backup_cb
,
1037 1000 * Settings
.BackupInterval
, data
);
1041 EnableAutosave (void)
1047 /* If we already have a timer going, then cancel it out */
1048 if (backup_timer
.ptr
!= NULL
&& gui
->stop_timer
)
1049 gui
->stop_timer (backup_timer
);
1051 backup_timer
.ptr
= NULL
;
1052 /* Start up a new timer */
1053 if (Settings
.BackupInterval
> 0 && gui
->add_timer
)
1054 backup_timer
= gui
->add_timer (backup_cb
,
1055 1000 * Settings
.BackupInterval
,
1059 /* ---------------------------------------------------------------------------
1060 * creates backup file. The default is to use the pcb file name with
1061 * a "-" appended (like "foo.pcb-") and if we don't have a pcb file name
1062 * then use the template in BACKUP_NAME
1067 char *filename
= NULL
;
1069 if( PCB
&& PCB
->Filename
)
1071 filename
= (char *) malloc (sizeof (char) * (strlen (PCB
->Filename
) + 2));
1072 if (filename
== NULL
)
1074 fprintf (stderr
, "Backup(): malloc failed\n");
1077 sprintf (filename
, "%s-", PCB
->Filename
);
1081 /* BACKUP_NAME has %.8i which will be replaced by the process ID */
1082 filename
= (char *) malloc (sizeof (char) * (strlen (BACKUP_NAME
) + 8));
1083 if (filename
== NULL
)
1085 fprintf (stderr
, "Backup(): malloc failed\n");
1088 sprintf (filename
, BACKUP_NAME
, (int) getpid ());
1091 WritePCBFile (filename
);
1095 #if !defined(HAS_ATEXIT) && !defined(HAS_ON_EXIT)
1096 /* ---------------------------------------------------------------------------
1097 * makes a temporary copy of the data. This is useful for systems which
1098 * doesn't support calling functions on exit. We use this to save the data
1099 * before LEX and YACC functions are called because they are able to abort
1105 sprintf (TMPFilename
, EMERGENCY_NAME
, (int) getpid ());
1106 WritePCBFile (TMPFilename
);
1109 /* ---------------------------------------------------------------------------
1110 * removes the temporary copy of the data file
1113 RemoveTMPData (void)
1115 unlink (TMPFilename
);
1119 /* ---------------------------------------------------------------------------
1120 * Parse the directory tree where newlib footprints are found
1123 /* Helper function for ParseLibraryTree */
1125 pcb_basename (char *p
)
1127 char *rv
= strrchr (p
, '/');
1133 /* This is a helper function for ParseLibrary Tree. Given a char *path,
1134 * it finds all newlib footprints in that dir and sticks them into the
1135 * library menu structure named entry.
1138 LoadNewlibFootprintsFromDir(char *libpath
, char *toppath
)
1140 char olddir
[MAXPATHLEN
+ 1]; /* The directory we start out in (cwd) */
1141 char subdir
[MAXPATHLEN
+ 1]; /* The directory holding footprints to load */
1142 DIR *subdirobj
; /* Interable object holding all subdir entries */
1143 struct dirent
*subdirentry
; /* Individual subdir entry */
1144 struct stat buffer
; /* Buffer used in stat */
1145 LibraryMenuTypePtr menu
= NULL
; /* Pointer to PCB's library menu structure */
1146 LibraryEntryTypePtr entry
; /* Pointer to individual menu entry */
1149 int n_footprints
= 0; /* Running count of footprints found in this subdir */
1151 /* Cache old dir, then cd into subdir because stat is given relative file names. */
1152 memset (subdir
, 0, sizeof subdir
);
1153 memset (olddir
, 0, sizeof olddir
);
1154 if (GetWorkingDirectory (olddir
) == NULL
)
1156 Message (_("LoadNewlibFootprintsFromDir: Could not determine initial working directory\n"));
1160 if (strcmp (libpath
, "(local)") == 0)
1161 strcpy (subdir
, ".");
1163 strcpy (subdir
, libpath
);
1167 ChdirErrorMessage (subdir
);
1171 /* Determine subdir is abs path */
1172 if (GetWorkingDirectory (subdir
) == NULL
)
1174 Message (_("LoadNewlibFootprintsFromDir: Could not determine new working directory\n"));
1176 ChdirErrorMessage (olddir
);
1180 /* First try opening the directory specified by path */
1181 if ( (subdirobj
= opendir (subdir
)) == NULL
)
1183 OpendirErrorMessage (subdir
);
1185 ChdirErrorMessage (olddir
);
1189 /* Get pointer to memory holding menu */
1190 menu
= GetLibraryMenuMemory (&Library
);
1191 /* Populate menuname and path vars */
1192 menu
->Name
= strdup (pcb_basename(subdir
));
1193 menu
->directory
= strdup (pcb_basename(toppath
));
1195 /* Now loop over files in this directory looking for files.
1196 * We ignore certain files which are not footprints.
1198 while ((subdirentry
= readdir (subdirobj
)) != NULL
)
1201 /* printf("... Examining file %s ... \n", subdirentry->d_name); */
1204 /* Ignore non-footprint files found in this directory
1205 * We're skipping .png and .html because those
1206 * may exist in a library tree to provide an html browsable
1207 * index of the library.
1209 l
= strlen (subdirentry
->d_name
);
1210 if (!stat (subdirentry
->d_name
, &buffer
) && S_ISREG (buffer
.st_mode
)
1211 && subdirentry
->d_name
[0] != '.'
1212 && NSTRCMP (subdirentry
->d_name
, "CVS") != 0
1213 && NSTRCMP (subdirentry
->d_name
, "Makefile") != 0
1214 && NSTRCMP (subdirentry
->d_name
, "Makefile.am") != 0
1215 && NSTRCMP (subdirentry
->d_name
, "Makefile.in") != 0
1216 && (l
< 4 || NSTRCMP(subdirentry
->d_name
+ (l
- 4), ".png") != 0)
1217 && (l
< 5 || NSTRCMP(subdirentry
->d_name
+ (l
- 5), ".html") != 0) )
1220 /* printf("... Found a footprint %s ... \n", subdirentry->d_name); */
1223 entry
= GetLibraryEntryMemory (menu
);
1226 * entry->AllocatedMemory points to abs path to the footprint.
1227 * entry->ListEntry points to fp name itself.
1229 len
= strlen(subdir
) + strlen("/") + strlen(subdirentry
->d_name
) + 1;
1230 entry
->AllocatedMemory
= calloc (1, len
);
1231 strcat (entry
->AllocatedMemory
, subdir
);
1232 strcat (entry
->AllocatedMemory
, PCB_DIR_SEPARATOR_S
);
1234 /* store pointer to start of footprint name */
1235 entry
->ListEntry
= entry
->AllocatedMemory
1236 + strlen (entry
->AllocatedMemory
);
1238 /* Now place footprint name into AllocatedMemory */
1239 strcat (entry
->AllocatedMemory
, subdirentry
->d_name
);
1241 /* mark as directory tree (newlib) library */
1242 entry
->Template
= (char *) -1;
1245 /* Done. Clean up, cd back into old dir, and return */
1246 closedir (subdirobj
);
1248 ChdirErrorMessage (olddir
);
1249 return n_footprints
;
1253 /* This function loads the newlib footprints into the Library.
1254 * It examines all directories pointed to by Settings.LibraryTree.
1255 * In each directory specified there, it looks both in that directory,
1256 * as well as *one* level down. It calls the subfunction
1257 * LoadNewlibFootprintsFromDir to put the footprints into PCB's internal
1261 ParseLibraryTree (void)
1263 char toppath
[MAXPATHLEN
+ 1]; /* String holding abs path to top level library dir */
1264 char working
[MAXPATHLEN
+ 1]; /* String holding abs path to working dir */
1265 char *libpaths
; /* String holding list of library paths to search */
1266 char *p
; /* Helper string used in iteration */
1267 DIR *dirobj
; /* Iterable directory object */
1268 struct dirent
*direntry
= NULL
; /* Object holding individual directory entries */
1269 struct stat buffer
; /* buffer used in stat */
1270 int n_footprints
= 0; /* Running count of footprints found */
1272 /* Initialize path, working by writing 0 into every byte. */
1273 memset (toppath
, 0, sizeof toppath
);
1274 memset (working
, 0, sizeof working
);
1276 /* Save the current working directory as an absolute path.
1277 * This fcn writes the abs path into the memory pointed to by the input arg.
1279 if (GetWorkingDirectory (working
) == NULL
)
1281 Message (_("ParseLibraryTree: Could not determine initial working directory\n"));
1285 /* Additional loop to allow for multiple 'newlib' style library directories
1286 * called out in Settings.LibraryTree
1288 libpaths
= strdup (Settings
.LibraryTree
);
1289 for (p
= strtok (libpaths
, PCB_PATH_DELIMETER
); p
&& *p
; p
= strtok (NULL
, PCB_PATH_DELIMETER
))
1291 /* remove trailing path delimeter */
1292 strncpy (toppath
, p
, sizeof (toppath
) - 1);
1294 /* start out in the working directory in case the path is a
1297 if (chdir (working
))
1299 ChdirErrorMessage (working
);
1305 * Next change to the directory which is the top of the library tree
1306 * and extract its abs path.
1308 if (chdir (toppath
))
1310 ChdirErrorMessage (toppath
);
1314 if (GetWorkingDirectory (toppath
) == NULL
)
1316 Message (_("ParseLibraryTree: Could not determine new working directory\n"));
1321 printf("In ParseLibraryTree, looking for newlib footprints inside top level directory %s ... \n",
1325 /* Next read in any footprints in the top level dir */
1326 n_footprints
+= LoadNewlibFootprintsFromDir("(local)", toppath
);
1328 /* Then open this dir so we can loop over its contents. */
1329 if ((dirobj
= opendir (toppath
)) == NULL
)
1331 OpendirErrorMessage (toppath
);
1335 /* Now loop over files in this directory looking for subdirs.
1336 * For each direntry which is a valid subdirectory,
1337 * try to load newlib footprints inside it.
1339 while ((direntry
= readdir (dirobj
)) != NULL
)
1342 printf("In ParseLibraryTree loop examining 2nd level direntry %s ... \n", direntry
->d_name
);
1344 /* Find subdirectories. Ignore entries beginning with "." and CVS
1347 if (!stat (direntry
->d_name
, &buffer
)
1348 && S_ISDIR (buffer
.st_mode
)
1349 && direntry
->d_name
[0] != '.'
1350 && NSTRCMP (direntry
->d_name
, "CVS") != 0)
1352 /* Found a valid subdirectory. Try to load footprints from it.
1354 n_footprints
+= LoadNewlibFootprintsFromDir(direntry
->d_name
, toppath
);
1360 /* restore the original working directory */
1361 if (chdir (working
))
1362 ChdirErrorMessage (working
);
1365 printf("Leaving ParseLibraryTree, found %d footprints.\n", n_footprints
);
1368 return n_footprints
;
1371 /* ---------------------------------------------------------------------------
1372 * Read contents of the library description file (for M4)
1373 * and then read in M4 libs. Then call a fcn to read the newlib
1377 ReadLibraryContents (void)
1379 static char *command
= NULL
;
1380 char inputline
[MAX_LIBRARY_LINE_LENGTH
+ 1];
1381 FILE *resultFP
= NULL
;
1382 LibraryMenuTypePtr menu
= NULL
;
1383 LibraryEntryTypePtr entry
;
1386 /* First load the M4 stuff. The variable Settings.LibraryPath
1390 command
= EvaluateFilename (Settings
.LibraryContentsCommand
,
1391 Settings
.LibraryPath
, Settings
.LibraryFilename
,
1395 printf("In ReadLibraryContents, about to execute command %s\n", command
);
1398 /* This uses a pipe to execute a shell script which provides the names of
1399 * all M4 libs and footprints. The results are placed in resultFP.
1401 if (command
&& *command
&& (resultFP
= popen (command
, "r")) == NULL
)
1403 PopenErrorMessage (command
);
1406 /* the M4 library contents are separated by colons;
1407 * template : package : name : description
1409 while (resultFP
!= NULL
&& fgets (inputline
, MAX_LIBRARY_LINE_LENGTH
, resultFP
))
1411 size_t len
= strlen (inputline
);
1413 /* check for maximum linelength */
1417 if (inputline
[len
] != '\n')
1419 ("linelength (%i) exceeded; following characters will be ignored\n",
1420 MAX_LIBRARY_LINE_LENGTH
);
1422 inputline
[len
] = '\0';
1425 /* if the line defines a menu */
1426 if (!strncmp (inputline
, "TYPE=", 5))
1428 menu
= GetLibraryMenuMemory (&Library
);
1429 menu
->Name
= strdup (UNKNOWN (&inputline
[5]));
1430 menu
->directory
= strdup (Settings
.LibraryFilename
);
1434 /* allocate a new menu entry if not already done */
1437 menu
= GetLibraryMenuMemory (&Library
);
1438 menu
->Name
= strdup (UNKNOWN ((char *) NULL
));
1439 menu
->directory
= strdup (Settings
.LibraryFilename
);
1441 entry
= GetLibraryEntryMemory (menu
);
1442 entry
->AllocatedMemory
= strdup (inputline
);
1444 /* now break the line into pieces separated by colons */
1445 if ((entry
->Template
= strtok (entry
->AllocatedMemory
, ":")) !=
1447 if ((entry
->Package
= strtok (NULL
, ":")) != NULL
)
1448 if ((entry
->Value
= strtok (NULL
, ":")) != NULL
)
1449 entry
->Description
= strtok (NULL
, ":");
1451 /* create the list entry */
1452 len
= strlen (EMPTY (entry
->Value
)) +
1453 strlen (EMPTY (entry
->Description
)) + 4;
1454 entry
->ListEntry
= calloc (len
, sizeof (char));
1455 sprintf (entry
->ListEntry
,
1456 "%s, %s", EMPTY (entry
->Value
),
1457 EMPTY (entry
->Description
));
1460 if (resultFP
!= NULL
)
1463 /* Now after reading in the M4 libs, call a function to
1464 * read the newlib footprint libraries. Then sort the whole
1467 if (ParseLibraryTree () > 0 || resultFP
!= NULL
)
1469 sort_library (&Library
);
1476 #define BLANK(x) ((x) == ' ' || (x) == '\t' || (x) == '\n' \
1479 /* ---------------------------------------------------------------------------
1480 * Read in a netlist and store it in the netlist menu
1484 ReadNetlist (char *filename
)
1486 static char *command
= NULL
;
1487 char inputline
[MAX_NETLIST_LINE_LENGTH
+ 1];
1488 char temp
[MAX_NETLIST_LINE_LENGTH
+ 1];
1490 LibraryMenuTypePtr menu
= NULL
;
1491 LibraryEntryTypePtr entry
;
1492 int i
, j
, lines
, kind
;
1497 return (1); /* nothing to do */
1499 Message (_("Importing PCB netlist %s\n"), filename
);
1501 if (EMPTY_STRING_P (Settings
.RatCommand
))
1503 fp
= fopen (filename
, "r");
1506 Message("Cannot open %s for reading", filename
);
1514 command
= EvaluateFilename (Settings
.RatCommand
,
1515 Settings
.RatPath
, filename
, NULL
);
1517 /* open pipe to stdout of command */
1518 if (*command
== '\0' || (fp
= popen (command
, "r")) == NULL
)
1520 PopenErrorMessage (command
);
1525 /* kind = 0 is net name
1526 * kind = 1 is route style name
1527 * kind = 2 is connection
1530 while (fgets (inputline
, MAX_NETLIST_LINE_LENGTH
, fp
))
1532 size_t len
= strlen (inputline
);
1533 /* check for maximum length line */
1536 if (inputline
[--len
] != '\n')
1537 Message (_("Line length (%i) exceeded in netlist file.\n"
1538 "additional characters will be ignored.\n"),
1539 MAX_NETLIST_LINE_LENGTH
);
1541 inputline
[len
] = '\0';
1543 continued
= (inputline
[len
- 1] == '\\') ? true : false;
1545 inputline
[len
- 1] = '\0';
1548 while (inputline
[i
] != '\0')
1551 /* skip leading blanks */
1552 while (inputline
[i
] != '\0' && BLANK (inputline
[i
]))
1556 /* add two spaces for included/unincluded */
1560 while (!BLANK (inputline
[i
]))
1561 temp
[j
++] = inputline
[i
++];
1563 while (inputline
[i
] != '\0' && BLANK (inputline
[i
]))
1567 menu
= GetLibraryMenuMemory (&PCB
->NetlistLib
);
1568 menu
->Name
= strdup (temp
);
1574 if (kind
== 1 && strchr (temp
, '-') == NULL
)
1577 menu
->Style
= strdup (temp
);
1581 entry
= GetLibraryEntryMemory (menu
);
1582 entry
->ListEntry
= strdup (temp
);
1591 Message (_("Empty netlist file!\n"));
1603 static int ReadEdifNetlist (char *filename
);
1605 int ImportNetlist (char *filename
)
1613 if (!filename
) return (1); /* nothing to do */
1614 fp
= fopen (filename
, "r");
1615 if (!fp
) return (1); /* bad filename */
1616 i
= fread (buf
, 1, sizeof(buf
)-1, fp
);
1622 *p
= tolower ((int) *p
);
1625 p
= strstr (buf
, "edif");
1626 if (!p
) return ReadNetlist (filename
);
1627 else return ReadEdifNetlist (filename
);
1630 static int ReadEdifNetlist (char *filename
)
1632 Message (_("Importing edif netlist %s\n"), filename
);
1633 ParseEDIF(filename
, NULL
);