4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996,1997,1998,2005,2006 Thomas Nau
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * Contact addresses for paper mail and Email:
22 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
23 * Thomas.Nau@rz.uni-ulm.de
27 /* file save, load, merge ... routines
28 * getpid() needs a cast to (int) to get rid of compiler warnings
29 * on several architectures
36 #ifdef HAVE_SYS_PARAM_H
37 #include <sys/param.h>
48 #ifdef HAVE_SYS_SOCKET_H
49 #include <sys/socket.h>
54 #ifdef HAVE_NETINET_IN_H
55 #include <netinet/in.h>
80 #include "crosshair.h"
82 #include "edif_parse.h"
90 #include "pcb-printf.h"
97 #ifdef HAVE_LIBDMALLOC
101 #if !defined(HAS_ATEXIT) && !defined(HAS_ON_EXIT)
102 /* ---------------------------------------------------------------------------
103 * some local identifiers for OS without an atexit() or on_exit()
106 static char TMPFilename
[80];
109 /* ---------------------------------------------------------------------------
110 * some local prototypes
112 static void PrintQuotedString (FILE *, char *);
113 static void WritePCBInfoHeader (FILE *);
114 static void WritePCBDataHeader (FILE *);
115 static void WritePCBFontData (FILE *);
116 static void WriteViaData (FILE *, DataType
*);
117 static void WritePCBRatData (FILE *);
118 static void WriteElementData (FILE *, DataType
*);
119 static void WriteLayerData (FILE *, Cardinal
, LayerType
*);
120 static int WritePCB (FILE *);
121 static int WritePCBFile (char *);
122 static int WritePipe (char *, bool);
123 static int ParseLibraryTree (void);
124 static int LoadNewlibFootprintsFromDir(char *path
, char *toppath
);
125 static char *pcb_basename (char *p
);
127 /* ---------------------------------------------------------------------------
128 * Flag helper functions
131 #define F2S(OBJ, TYPE) flags_to_string ((OBJ)->Flags, TYPE)
133 /* --------------------------------------------------------------------------- */
135 /* The idea here is to avoid gratuitously breaking backwards
136 compatibility due to a new but rarely used feature. The first such
137 case, for example, was the polygon Hole - if your design included
138 polygon holes, you needed a newer PCB to read it, but if your
139 design didn't include holes, PCB would produce a file that older
140 PCBs could read, if only it had the correct version number in it.
142 If, however, you have to add or change a feature that really does
143 require a new PCB version all the time, it's time to remove all the
144 tests below and just always output the new version.
146 Note: Best practices here is to add support for a feature *first*
147 (and bump PCB_FILE_VERSION in file.h), and note the version that
148 added that support below, and *later* update the file format to
149 need that version (which may then be older than PCB_FILE_VERSION).
150 Hopefully, that allows for one release between adding support and
151 needing it, which should minimize breakage. Of course, that's not
152 *always* possible, practical, or desirable.
156 /* Hole[] in Polygon. */
157 #define PCB_FILE_VERSION_HOLES 20100606
158 /* First version ever saved. */
159 #define PCB_FILE_VERSION_BASELINE 20091103
162 PCBFileVersionNeeded (void)
164 ALLPOLYGON_LOOP (PCB
->Data
);
166 if (polygon
->HoleIndexN
> 0)
167 return PCB_FILE_VERSION_HOLES
;
171 return PCB_FILE_VERSION_BASELINE
;
174 /* --------------------------------------------------------------------------- */
177 string_cmp (const char *a
, const char *b
)
181 if (isdigit ((int) *a
) && isdigit ((int) *b
))
187 while (isdigit ((int) *a
) && *(a
+1))
189 while (isdigit ((int) *b
) && *(b
+1))
192 else if (tolower ((int) *a
) != tolower ((int) *b
))
193 return tolower ((int) *a
) - tolower ((int) *b
);
204 static int netlist_sort_offset
= 0;
207 netlist_sort (const void *va
, const void *vb
)
209 LibraryMenuType
*am
= (LibraryMenuType
*) va
;
210 LibraryMenuType
*bm
= (LibraryMenuType
*) vb
;
217 return string_cmp (a
, b
);
221 netnode_sort (const void *va
, const void *vb
)
223 LibraryEntryType
*am
= (LibraryEntryType
*) va
;
224 LibraryEntryType
*bm
= (LibraryEntryType
*) vb
;
225 char *a
= am
->ListEntry
;
226 char *b
= bm
->ListEntry
;
227 return string_cmp (a
, b
);
231 sort_library (LibraryType
*lib
)
234 qsort (lib
->Menu
, lib
->MenuN
, sizeof (lib
->Menu
[0]), netlist_sort
);
235 for (i
= 0; i
< lib
->MenuN
; i
++)
236 qsort (lib
->Menu
[i
].Entry
,
237 lib
->Menu
[i
].EntryN
, sizeof (lib
->Menu
[i
].Entry
[0]), netnode_sort
);
243 netlist_sort_offset
= 2;
244 sort_library (&(PCB
->NetlistLib
));
245 netlist_sort_offset
= 0;
248 /* ---------------------------------------------------------------------------
249 * opens a file and check if it exists
252 CheckAndOpenFile (char *Filename
, bool Confirm
, bool AllButton
,
253 bool * WasAllButton
, bool * WasCancelButton
)
257 char message
[MAXPATHLEN
+ 80];
260 if (Filename
&& *Filename
)
262 if (!stat (Filename
, &buffer
) && Confirm
)
264 sprintf (message
, _("File '%s' exists, use anyway?"), Filename
);
266 *WasAllButton
= false;
268 *WasCancelButton
= false;
271 gui
->confirm_dialog (message
, "Cancel", "Ok",
272 AllButton
? "Sequence OK" : 0);
275 gui
->confirm_dialog (message
, "Cancel", "Ok", "Sequence OK");
281 *WasAllButton
= true;
285 *WasCancelButton
= true;
288 if ((fp
= fopen (Filename
, "w")) == NULL
)
289 OpenErrorMessage (Filename
);
294 /* ---------------------------------------------------------------------------
295 * opens a file for saving connection data
298 OpenConnectionDataFile (void)
302 static char * default_file
= NULL
;
303 bool result
; /* not used */
305 /* CheckAndOpenFile deals with the case where fname already exists */
306 fname
= gui
->fileselect (_("Save Connection Data As ..."),
307 _("Choose a file to save all connection data to."),
308 default_file
, ".net", "connection_data",
313 if (default_file
!= NULL
)
320 default_file
= strdup (fname
);
322 fp
= CheckAndOpenFile (fname
, true, false, &result
, NULL
);
328 /* ---------------------------------------------------------------------------
329 * save elements in the current buffer
332 SaveBufferElements (char *Filename
)
338 result
= WritePipe (Filename
, false);
344 /* ---------------------------------------------------------------------------
352 if (gui
->notify_save_pcb
== NULL
)
353 return WritePipe (file
, true);
355 gui
->notify_save_pcb (file
, false);
356 retcode
= WritePipe (file
, true);
357 gui
->notify_save_pcb (file
, true);
362 /* ---------------------------------------------------------------------------
363 * set the route style to the first one, if the current one doesn't
364 * happen to match any. This way, "revert" won't change the route
368 set_some_route_style ()
370 if (hid_get_flag ("style"))
372 SetLineSize (PCB
->RouteStyle
[0].Thick
);
373 SetViaSize (PCB
->RouteStyle
[0].Diameter
, true);
374 SetViaDrillingHole (PCB
->RouteStyle
[0].Hole
, true);
375 SetKeepawayWidth (PCB
->RouteStyle
[0].Keepaway
);
378 /* ---------------------------------------------------------------------------
380 * parse the file with enabled 'PCB mode' (see parser)
381 * if successful, update some other stuff
383 * If revert is true, we pass "revert" as a parameter
384 * to the HID's PCBChanged action.
387 real_load_pcb (char *Filename
, bool revert
)
389 const char *unit_suffix
, *grid_size
;
391 PCBType
*newPCB
= CreateNewPCB (false);
400 new_filename
= strdup (Filename
);
405 /* mark the default font invalid to know if the file has one */
406 newPCB
->Font
.Valid
= false;
408 /* new data isn't added to the undo list */
409 if (!ParsePCB (PCB
, new_filename
))
413 CreateNewPCBPost (PCB
, 0);
414 ResetStackAndVisibility ();
416 /* update cursor location */
417 Crosshair
.X
= CLAMP (PCB
->CursorX
, 0, PCB
->MaxWidth
);
418 Crosshair
.Y
= CLAMP (PCB
->CursorY
, 0, PCB
->MaxHeight
);
420 /* update cursor confinement and output area (scrollbars) */
421 ChangePCBSize (PCB
->MaxWidth
, PCB
->MaxHeight
);
423 /* enable default font if necessary */
424 if (!PCB
->Font
.Valid
)
427 ("File '%s' has no font information, using default font\n"),
429 PCB
->Font
.Valid
= true;
432 /* clear 'changed flag' */
433 SetChangedFlag (false);
434 PCB
->Filename
= new_filename
;
435 /* just in case a bad file saved file is loaded */
437 /* Use attribute PCB::grid::unit as unit, if we can */
438 unit_suffix
= AttributeGet (PCB
, "PCB::grid::unit");
439 if (unit_suffix
&& *unit_suffix
)
441 const Unit
*new_unit
= get_unit_struct (unit_suffix
);
443 Settings
.grid_unit
= new_unit
;
445 AttributePut (PCB
, "PCB::grid::unit", Settings
.grid_unit
->suffix
);
446 /* Use attribute PCB::grid::size as size, if we can */
447 grid_size
= AttributeGet (PCB
, "PCB::grid::size");
450 PCB
->Grid
= GetValue (grid_size
, NULL
, NULL
);
455 set_some_route_style ();
458 hid_actionl ("PCBChanged", "revert", NULL
);
460 hid_action ("PCBChanged");
464 elapsed
= ((double) (end
- start
)) / CLOCKS_PER_SEC
;
465 gui
->log ("Loading file %s took %f seconds of CPU time\n",
466 new_filename
, elapsed
);
472 hid_action ("PCBChanged");
474 /* release unused memory */
479 /* ---------------------------------------------------------------------------
485 return real_load_pcb (file
, false);
488 /* ---------------------------------------------------------------------------
494 return real_load_pcb (PCB
->Filename
, true);
497 /* ---------------------------------------------------------------------------
498 * functions for loading elements-as-pcb
501 extern PCBType
* yyPCB
;
502 extern DataType
* yyData
;
503 extern FontType
* yyFont
;
512 yyFont
= &yyPCB
->Font
;
513 yyData
= yyPCB
->Data
;
519 PostLoadElementPCB ()
521 PCBType
*pcb_save
= PCB
;
527 CreateNewPCBPost (yyPCB
, 0);
528 ParseGroupString("1,c:2,s", &yyPCB
->LayerGroups
, yyData
->LayerN
);
529 e
= yyPCB
->Data
->Element
->data
; /* we know there's only one */
531 MoveElementLowLevel (yyPCB
->Data
,
532 e
, -e
->BoundingBox
.X1
, -e
->BoundingBox
.Y1
);
534 yyPCB
->MaxWidth
= e
->BoundingBox
.X2
;
535 yyPCB
->MaxHeight
= e
->BoundingBox
.Y2
;
536 yyPCB
->is_footprint
= 1;
539 /* ---------------------------------------------------------------------------
540 * writes the quoted string created by another subroutine
543 PrintQuotedString (FILE * FP
, char *S
)
545 static DynamicStringType ds
;
547 CreateQuotedString (&ds
, S
);
551 /* ---------------------------------------------------------------------------
552 * writes out an attribute list
555 WriteAttributeList (FILE * FP
, AttributeListType
*list
, char *prefix
)
559 for (i
= 0; i
< list
->Number
; i
++)
560 fprintf (FP
, "%sAttribute(\"%s\" \"%s\")\n",
561 prefix
, list
->List
[i
].name
, list
->List
[i
].value
);
564 /* ---------------------------------------------------------------------------
565 * writes layout header information
568 WritePCBInfoHeader (FILE * FP
)
570 /* write some useful comments */
571 fprintf (FP
, "# release: %s " VERSION
"\n", Progname
);
573 /* avoid writing things like user name or date, as these cause merge
574 * conflicts in collaborative environments using version control systems
578 /* ---------------------------------------------------------------------------
580 * the name of the PCB, cursor location, zoom and grid
581 * layergroups and some flags
584 WritePCBDataHeader (FILE * FP
)
589 * ************************** README *******************
590 * ************************** README *******************
592 * If the file format is modified in any way, update
593 * PCB_FILE_VERSION in file.h as well as PCBFileVersionNeeded()
594 * at the top of this file.
596 * ************************** README *******************
597 * ************************** README *******************
600 fprintf (FP
, "\n# To read pcb files, the pcb version (or the git source date) must be >= the file version\n");
601 fprintf (FP
, "FileVersion[%i]\n", PCBFileVersionNeeded ());
603 fputs ("\nPCB[", FP
);
604 PrintQuotedString (FP
, (char *)EMPTY (PCB
->Name
));
605 pcb_fprintf (FP
, " %mr %mr]\n\n", PCB
->MaxWidth
, PCB
->MaxHeight
);
606 pcb_fprintf (FP
, "Grid[%s %mr %mr %d]\n", c_dtostr (COORD_TO_MIL (PCB
->Grid
) * 100), PCB
->GridOffsetX
, PCB
->GridOffsetY
, Settings
.DrawGrid
);
607 /* PolyArea should be output in square cmils, no suffix */
608 fprintf (FP
, "PolyArea[%s]\n", c_dtostr (COORD_TO_MIL (COORD_TO_MIL (PCB
->IsleArea
) * 100) * 100));
609 pcb_fprintf (FP
, "Thermal[%s]\n", c_dtostr (PCB
->ThermScale
));
610 pcb_fprintf (FP
, "DRC[%mr %mr %mr %mr %mr %mr]\n", PCB
->Bloat
, PCB
->Shrink
,
611 PCB
->minWid
, PCB
->minSlk
, PCB
->minDrill
, PCB
->minRing
);
612 fprintf (FP
, "Flags(%s)\n", pcbflags_to_string(PCB
->Flags
));
613 fprintf (FP
, "Groups(\"%s\")\n", LayerGroupsToString (&PCB
->LayerGroups
));
614 fputs ("Styles[\"", FP
);
615 for (group
= 0; group
< NUM_STYLES
- 1; group
++)
616 pcb_fprintf (FP
, "%s,%mr,%mr,%mr,%mr:", PCB
->RouteStyle
[group
].Name
,
617 PCB
->RouteStyle
[group
].Thick
,
618 PCB
->RouteStyle
[group
].Diameter
,
619 PCB
->RouteStyle
[group
].Hole
, PCB
->RouteStyle
[group
].Keepaway
);
620 pcb_fprintf (FP
, "%s,%mr,%mr,%mr,%mr\"]\n\n", PCB
->RouteStyle
[group
].Name
,
621 PCB
->RouteStyle
[group
].Thick
,
622 PCB
->RouteStyle
[group
].Diameter
,
623 PCB
->RouteStyle
[group
].Hole
, PCB
->RouteStyle
[group
].Keepaway
);
626 /* ---------------------------------------------------------------------------
627 * writes font data of non empty symbols
630 WritePCBFontData (FILE * FP
)
636 for (font
= &PCB
->Font
, i
= 0; i
<= MAX_FONTPOSITION
; i
++)
638 if (!font
->Symbol
[i
].Valid
)
642 pcb_fprintf (FP
, "Symbol['%c' %mr]\n(\n", i
, font
->Symbol
[i
].Delta
);
644 pcb_fprintf (FP
, "Symbol[%i %mr]\n(\n", i
, font
->Symbol
[i
].Delta
);
646 line
= font
->Symbol
[i
].Line
;
647 for (j
= font
->Symbol
[i
].LineN
; j
; j
--, line
++)
648 pcb_fprintf (FP
, "\tSymbolLine[%mr %mr %mr %mr %mr]\n",
649 line
->Point1
.X
, line
->Point1
.Y
,
650 line
->Point2
.X
, line
->Point2
.Y
, line
->Thickness
);
655 /* ---------------------------------------------------------------------------
659 WriteViaData (FILE * FP
, DataType
*Data
)
662 /* write information about vias */
663 for (iter
= Data
->Via
; iter
!= NULL
; iter
= g_list_next (iter
))
665 PinType
*via
= iter
->data
;
666 pcb_fprintf (FP
, "Via[%mr %mr %mr %mr %mr %mr ", via
->X
, via
->Y
,
667 via
->Thickness
, via
->Clearance
, via
->Mask
, via
->DrillingHole
);
668 PrintQuotedString (FP
, (char *)EMPTY (via
->Name
));
669 fprintf (FP
, " %s]\n", F2S (via
, VIA_TYPE
));
673 /* ---------------------------------------------------------------------------
674 * writes rat-line data
677 WritePCBRatData (FILE * FP
)
680 /* write information about rats */
681 for (iter
= PCB
->Data
->Rat
; iter
!= NULL
; iter
= g_list_next (iter
))
683 RatType
*line
= iter
->data
;
684 pcb_fprintf (FP
, "Rat[%mr %mr %d %mr %mr %d ",
685 line
->Point1
.X
, line
->Point1
.Y
, line
->group1
,
686 line
->Point2
.X
, line
->Point2
.Y
, line
->group2
);
687 fprintf (FP
, " %s]\n", F2S (line
, RATLINE_TYPE
));
691 /* ---------------------------------------------------------------------------
692 * writes netlist data
695 WritePCBNetlistData (FILE * FP
)
697 /* write out the netlist if it exists */
698 if (PCB
->NetlistLib
.MenuN
)
701 fprintf (FP
, "NetList()\n(\n");
703 for (n
= 0; n
< PCB
->NetlistLib
.MenuN
; n
++)
705 LibraryMenuType
*menu
= &PCB
->NetlistLib
.Menu
[n
];
706 fprintf (FP
, "\tNet(");
707 PrintQuotedString(FP
, &menu
->Name
[2]);
709 PrintQuotedString(FP
, (char *)UNKNOWN (menu
->Style
));
710 fprintf (FP
, ")\n\t(\n");
711 for (p
= 0; p
< menu
->EntryN
; p
++)
713 LibraryEntryType
*entry
= &menu
->Entry
[p
];
714 fprintf (FP
, "\t\tConnect(");
715 PrintQuotedString (FP
, entry
->ListEntry
);
718 fprintf (FP
, "\t)\n");
724 /* ---------------------------------------------------------------------------
725 * writes element data
728 WriteElementData (FILE * FP
, DataType
*Data
)
731 for (n
= Data
->Element
; n
!= NULL
; n
= g_list_next (n
))
733 ElementType
*element
= n
->data
;
735 /* only non empty elements */
736 if (!element
->LineN
&& !element
->PinN
&& !element
->ArcN
739 /* the coordinates and text-flags are the same for
740 * both names of an element
742 fprintf (FP
, "\nElement[%s ", F2S (element
, ELEMENT_TYPE
));
743 PrintQuotedString (FP
, (char *)EMPTY (DESCRIPTION_NAME (element
)));
745 PrintQuotedString (FP
, (char *)EMPTY (NAMEONPCB_NAME (element
)));
747 PrintQuotedString (FP
, (char *)EMPTY (VALUE_NAME (element
)));
748 pcb_fprintf (FP
, " %mr %mr %mr %mr %d %d %s]\n(\n",
749 element
->MarkX
, element
->MarkY
,
750 DESCRIPTION_TEXT (element
).X
- element
->MarkX
,
751 DESCRIPTION_TEXT (element
).Y
- element
->MarkY
,
752 DESCRIPTION_TEXT (element
).Direction
,
753 DESCRIPTION_TEXT (element
).Scale
,
754 F2S (&(DESCRIPTION_TEXT (element
)), ELEMENTNAME_TYPE
));
755 WriteAttributeList (FP
, &element
->Attributes
, "\t");
756 for (p
= element
->Pin
; p
!= NULL
; p
= g_list_next (p
))
758 PinType
*pin
= p
->data
;
759 pcb_fprintf (FP
, "\tPin[%mr %mr %mr %mr %mr %mr ",
760 pin
->X
- element
->MarkX
,
761 pin
->Y
- element
->MarkY
,
762 pin
->Thickness
, pin
->Clearance
,
763 pin
->Mask
, pin
->DrillingHole
);
764 PrintQuotedString (FP
, (char *)EMPTY (pin
->Name
));
766 PrintQuotedString (FP
, (char *)EMPTY (pin
->Number
));
767 fprintf (FP
, " %s]\n", F2S (pin
, PIN_TYPE
));
769 for (p
= element
->Pad
; p
!= NULL
; p
= g_list_next (p
))
771 PadType
*pad
= p
->data
;
772 pcb_fprintf (FP
, "\tPad[%mr %mr %mr %mr %mr %mr %mr ",
773 pad
->Point1
.X
- element
->MarkX
,
774 pad
->Point1
.Y
- element
->MarkY
,
775 pad
->Point2
.X
- element
->MarkX
,
776 pad
->Point2
.Y
- element
->MarkY
,
777 pad
->Thickness
, pad
->Clearance
, pad
->Mask
);
778 PrintQuotedString (FP
, (char *)EMPTY (pad
->Name
));
780 PrintQuotedString (FP
, (char *)EMPTY (pad
->Number
));
781 fprintf (FP
, " %s]\n", F2S (pad
, PAD_TYPE
));
783 for (p
= element
->Line
; p
!= NULL
; p
= g_list_next (p
))
785 LineType
*line
= p
->data
;
786 pcb_fprintf (FP
, "\tElementLine [%mr %mr %mr %mr %mr]\n",
787 line
->Point1
.X
- element
->MarkX
,
788 line
->Point1
.Y
- element
->MarkY
,
789 line
->Point2
.X
- element
->MarkX
,
790 line
->Point2
.Y
- element
->MarkY
,
793 for (p
= element
->Arc
; p
!= NULL
; p
= g_list_next (p
))
795 ArcType
*arc
= p
->data
;
796 pcb_fprintf (FP
, "\tElementArc [%mr %mr %mr %mr %ma %ma %mr]\n",
797 arc
->X
- element
->MarkX
,
798 arc
->Y
- element
->MarkY
,
799 arc
->Width
, arc
->Height
,
800 arc
->StartAngle
, arc
->Delta
,
803 fputs ("\n\t)\n", FP
);
807 /* ---------------------------------------------------------------------------
811 WriteLayerData (FILE * FP
, Cardinal Number
, LayerType
*layer
)
814 /* write information about non empty layers */
815 if (layer
->LineN
|| layer
->ArcN
|| layer
->TextN
|| layer
->PolygonN
||
816 (layer
->Name
&& *layer
->Name
))
818 fprintf (FP
, "Layer(%i ", (int) Number
+ 1);
819 PrintQuotedString (FP
, (char *)EMPTY (layer
->Name
));
820 fputs (")\n(\n", FP
);
821 WriteAttributeList (FP
, &layer
->Attributes
, "\t");
823 for (n
= layer
->Line
; n
!= NULL
; n
= g_list_next (n
))
825 LineType
*line
= n
->data
;
826 pcb_fprintf (FP
, "\tLine[%mr %mr %mr %mr %mr %mr %s]\n",
827 line
->Point1
.X
, line
->Point1
.Y
,
828 line
->Point2
.X
, line
->Point2
.Y
,
829 line
->Thickness
, line
->Clearance
,
830 F2S (line
, LINE_TYPE
));
832 for (n
= layer
->Arc
; n
!= NULL
; n
= g_list_next (n
))
834 ArcType
*arc
= n
->data
;
835 pcb_fprintf (FP
, "\tArc[%mr %mr %mr %mr %mr %mr %ma %ma %s]\n",
836 arc
->X
, arc
->Y
, arc
->Width
,
837 arc
->Height
, arc
->Thickness
,
838 arc
->Clearance
, arc
->StartAngle
,
839 arc
->Delta
, F2S (arc
, ARC_TYPE
));
841 for (n
= layer
->Text
; n
!= NULL
; n
= g_list_next (n
))
843 TextType
*text
= n
->data
;
844 pcb_fprintf (FP
, "\tText[%mr %mr %d %d ",
846 text
->Direction
, text
->Scale
);
847 PrintQuotedString (FP
, (char *)EMPTY (text
->TextString
));
848 fprintf (FP
, " %s]\n", F2S (text
, TEXT_TYPE
));
850 for (n
= layer
->Polygon
; n
!= NULL
; n
= g_list_next (n
))
852 PolygonType
*polygon
= n
->data
;
855 fprintf (FP
, "\tPolygon(%s)\n\t(", F2S (polygon
, POLYGON_TYPE
));
856 for (p
= 0; p
< polygon
->PointN
; p
++)
858 PointType
*point
= &polygon
->Points
[p
];
860 if (hole
< polygon
->HoleIndexN
&&
861 p
== polygon
->HoleIndex
[hole
])
864 fputs ("\n\t\t)", FP
);
865 fputs ("\n\t\tHole (", FP
);
872 fputs ("\n\t\t", FP
);
876 pcb_fprintf (FP
, "[%mr %mr] ", point
->X
, point
->Y
);
879 fputs ("\n\t\t)", FP
);
880 fputs ("\n\t)\n", FP
);
886 /* ---------------------------------------------------------------------------
887 * writes just the elements in the buffer to file
890 WriteBuffer (FILE * FP
)
894 WriteViaData (FP
, PASTEBUFFER
->Data
);
895 WriteElementData (FP
, PASTEBUFFER
->Data
);
896 for (i
= 0; i
< max_copper_layer
+ 2; i
++)
897 WriteLayerData (FP
, i
, &(PASTEBUFFER
->Data
->Layer
[i
]));
901 /* ---------------------------------------------------------------------------
909 WritePCBInfoHeader (FP
);
910 WritePCBDataHeader (FP
);
911 WritePCBFontData (FP
);
912 WriteAttributeList (FP
, &PCB
->Attributes
, "");
913 WriteViaData (FP
, PCB
->Data
);
914 WriteElementData (FP
, PCB
->Data
);
915 WritePCBRatData (FP
);
916 for (i
= 0; i
< max_copper_layer
+ 2; i
++)
917 WriteLayerData (FP
, i
, &(PCB
->Data
->Layer
[i
]));
918 WritePCBNetlistData (FP
);
923 /* ---------------------------------------------------------------------------
927 WritePCBFile (char *Filename
)
932 if ((fp
= fopen (Filename
, "w")) == NULL
)
934 OpenErrorMessage (Filename
);
935 return (STATUS_ERROR
);
937 result
= WritePCB (fp
);
942 /* ---------------------------------------------------------------------------
943 * writes to pipe using the command defined by Settings.SaveCommand
944 * %f are replaced by the passed filename
947 WritePipe (char *Filename
, bool thePcb
)
952 static DynamicStringType command
;
955 if (EMPTY_STRING_P (Settings
.SaveCommand
))
957 fp
= fopen (Filename
, "w");
960 Message ("Unable to write to file %s\n", Filename
);
967 /* setup commandline */
968 DSClearString (&command
);
969 for (p
= Settings
.SaveCommand
; *p
; p
++)
971 /* copy character if not special or add string to command */
972 if (!(*p
== '%' && *(p
+ 1) == 'f'))
973 DSAddCharacter (&command
, *p
);
976 DSAddString (&command
, Filename
);
978 /* skip the character */
982 DSAddCharacter (&command
, '\0');
983 printf ("write to pipe \"%s\"\n", command
.Data
);
984 if ((fp
= popen (command
.Data
, "w")) == NULL
)
986 PopenErrorMessage (command
.Data
);
987 return (STATUS_ERROR
);
992 if (PCB
->is_footprint
)
994 WriteElementData (fp
, PCB
->Data
);
998 result
= WritePCB (fp
);
1001 result
= WriteBuffer (fp
);
1004 return (pclose (fp
) ? STATUS_ERROR
: result
);
1005 return (fclose (fp
) ? STATUS_ERROR
: result
);
1008 /* ---------------------------------------------------------------------------
1009 * saves the layout in a temporary file
1010 * this is used for fatal errors and does not call the program specified
1011 * in 'saveCommand' for safety reasons
1018 /* memory might have been released before this function is called */
1019 if (PCB
&& PCB
->Changed
)
1021 sprintf (filename
, EMERGENCY_NAME
, (int) getpid ());
1022 Message (_("Trying to save your layout in '%s'\n"), filename
);
1023 WritePCBFile (filename
);
1027 /* ---------------------------------------------------------------------------
1028 * front-end for 'SaveInTMP()'
1029 * just makes sure that the routine is only called once
1031 static bool dont_save_any_more
= false;
1033 EmergencySave (void)
1036 if (!dont_save_any_more
)
1039 dont_save_any_more
= true;
1044 DisableEmergencySave (void)
1046 dont_save_any_more
= true;
1049 /* ----------------------------------------------------------------------
1050 * Callback for the autosave
1053 static hidval backup_timer
;
1056 * If the backup interval is > 0 then set another timer. Otherwise
1057 * we do nothing and it is up to the GUI to call EnableAutosave()
1058 * after setting Settings.BackupInterval > 0 again.
1061 backup_cb (hidval data
)
1063 backup_timer
.ptr
= NULL
;
1065 if (Settings
.BackupInterval
> 0 && gui
->add_timer
)
1066 backup_timer
= gui
->add_timer (backup_cb
,
1067 1000 * Settings
.BackupInterval
, data
);
1071 EnableAutosave (void)
1077 /* If we already have a timer going, then cancel it out */
1078 if (backup_timer
.ptr
!= NULL
&& gui
->stop_timer
)
1079 gui
->stop_timer (backup_timer
);
1081 backup_timer
.ptr
= NULL
;
1082 /* Start up a new timer */
1083 if (Settings
.BackupInterval
> 0 && gui
->add_timer
)
1084 backup_timer
= gui
->add_timer (backup_cb
,
1085 1000 * Settings
.BackupInterval
,
1089 /* ---------------------------------------------------------------------------
1090 * creates backup file. The default is to use the pcb file name with
1091 * a "-" appended (like "foo.pcb-") and if we don't have a pcb file name
1092 * then use the template in BACKUP_NAME
1097 char *filename
= NULL
;
1099 if( PCB
&& PCB
->Filename
)
1101 filename
= (char *) malloc (sizeof (char) * (strlen (PCB
->Filename
) + 2));
1102 if (filename
== NULL
)
1104 fprintf (stderr
, "Backup(): malloc failed\n");
1107 sprintf (filename
, "%s-", PCB
->Filename
);
1111 /* BACKUP_NAME has %.8i which will be replaced by the process ID */
1112 filename
= (char *) malloc (sizeof (char) * (strlen (BACKUP_NAME
) + 8));
1113 if (filename
== NULL
)
1115 fprintf (stderr
, "Backup(): malloc failed\n");
1118 sprintf (filename
, BACKUP_NAME
, (int) getpid ());
1121 WritePCBFile (filename
);
1125 #if !defined(HAS_ATEXIT) && !defined(HAS_ON_EXIT)
1126 /* ---------------------------------------------------------------------------
1127 * makes a temporary copy of the data. This is useful for systems which
1128 * doesn't support calling functions on exit. We use this to save the data
1129 * before LEX and YACC functions are called because they are able to abort
1135 sprintf (TMPFilename
, EMERGENCY_NAME
, (int) getpid ());
1136 WritePCBFile (TMPFilename
);
1139 /* ---------------------------------------------------------------------------
1140 * removes the temporary copy of the data file
1143 RemoveTMPData (void)
1145 unlink (TMPFilename
);
1149 /* ---------------------------------------------------------------------------
1150 * Parse the directory tree where newlib footprints are found
1153 /* Helper function for ParseLibraryTree */
1155 pcb_basename (char *p
)
1157 char *rv
= strrchr (p
, '/');
1163 /* This is a helper function for ParseLibrary Tree. Given a char *path,
1164 * it finds all newlib footprints in that dir and sticks them into the
1165 * library menu structure named entry.
1168 LoadNewlibFootprintsFromDir(char *libpath
, char *toppath
)
1170 char olddir
[MAXPATHLEN
+ 1]; /* The directory we start out in (cwd) */
1171 char subdir
[MAXPATHLEN
+ 1]; /* The directory holding footprints to load */
1172 DIR *subdirobj
; /* Interable object holding all subdir entries */
1173 struct dirent
*subdirentry
; /* Individual subdir entry */
1174 struct stat buffer
; /* Buffer used in stat */
1175 LibraryMenuType
*menu
= NULL
; /* Pointer to PCB's library menu structure */
1176 LibraryEntryType
*entry
; /* Pointer to individual menu entry */
1179 int n_footprints
= 0; /* Running count of footprints found in this subdir */
1181 /* Cache old dir, then cd into subdir because stat is given relative file names. */
1182 memset (subdir
, 0, sizeof subdir
);
1183 memset (olddir
, 0, sizeof olddir
);
1184 if (GetWorkingDirectory (olddir
) == NULL
)
1186 Message (_("LoadNewlibFootprintsFromDir: Could not determine initial working directory\n"));
1190 if (strcmp (libpath
, "(local)") == 0)
1191 strcpy (subdir
, ".");
1193 strcpy (subdir
, libpath
);
1197 ChdirErrorMessage (subdir
);
1201 /* Determine subdir is abs path */
1202 if (GetWorkingDirectory (subdir
) == NULL
)
1204 Message (_("LoadNewlibFootprintsFromDir: Could not determine new working directory\n"));
1206 ChdirErrorMessage (olddir
);
1210 /* First try opening the directory specified by path */
1211 if ( (subdirobj
= opendir (subdir
)) == NULL
)
1213 OpendirErrorMessage (subdir
);
1215 ChdirErrorMessage (olddir
);
1219 /* Get pointer to memory holding menu */
1220 menu
= GetLibraryMenuMemory (&Library
);
1221 /* Populate menuname and path vars */
1222 menu
->Name
= strdup (pcb_basename(subdir
));
1223 menu
->directory
= strdup (pcb_basename(toppath
));
1225 /* Now loop over files in this directory looking for files.
1226 * We ignore certain files which are not footprints.
1228 while ((subdirentry
= readdir (subdirobj
)) != NULL
)
1231 /* printf("... Examining file %s ... \n", subdirentry->d_name); */
1234 /* Ignore non-footprint files found in this directory
1235 * We're skipping .png and .html because those
1236 * may exist in a library tree to provide an html browsable
1237 * index of the library.
1239 l
= strlen (subdirentry
->d_name
);
1240 if (!stat (subdirentry
->d_name
, &buffer
) && S_ISREG (buffer
.st_mode
)
1241 && subdirentry
->d_name
[0] != '.'
1242 && NSTRCMP (subdirentry
->d_name
, "CVS") != 0
1243 && NSTRCMP (subdirentry
->d_name
, "Makefile") != 0
1244 && NSTRCMP (subdirentry
->d_name
, "Makefile.am") != 0
1245 && NSTRCMP (subdirentry
->d_name
, "Makefile.in") != 0
1246 && (l
< 4 || NSTRCMP(subdirentry
->d_name
+ (l
- 4), ".png") != 0)
1247 && (l
< 5 || NSTRCMP(subdirentry
->d_name
+ (l
- 5), ".html") != 0)
1248 && (l
< 4 || NSTRCMP(subdirentry
->d_name
+ (l
- 4), ".pcb") != 0) )
1251 /* printf("... Found a footprint %s ... \n", subdirentry->d_name); */
1254 entry
= GetLibraryEntryMemory (menu
);
1257 * entry->AllocatedMemory points to abs path to the footprint.
1258 * entry->ListEntry points to fp name itself.
1260 len
= strlen(subdir
) + strlen("/") + strlen(subdirentry
->d_name
) + 1;
1261 entry
->AllocatedMemory
= (char *)calloc (1, len
);
1262 strcat (entry
->AllocatedMemory
, subdir
);
1263 strcat (entry
->AllocatedMemory
, PCB_DIR_SEPARATOR_S
);
1265 /* store pointer to start of footprint name */
1266 entry
->ListEntry
= entry
->AllocatedMemory
1267 + strlen (entry
->AllocatedMemory
);
1269 /* Now place footprint name into AllocatedMemory */
1270 strcat (entry
->AllocatedMemory
, subdirentry
->d_name
);
1272 /* mark as directory tree (newlib) library */
1273 entry
->Template
= (char *) -1;
1276 /* Done. Clean up, cd back into old dir, and return */
1277 closedir (subdirobj
);
1279 ChdirErrorMessage (olddir
);
1280 return n_footprints
;
1284 /* This function loads the newlib footprints into the Library.
1285 * It examines all directories pointed to by Settings.LibraryTree.
1286 * In each directory specified there, it looks both in that directory,
1287 * as well as *one* level down. It calls the subfunction
1288 * LoadNewlibFootprintsFromDir to put the footprints into PCB's internal
1292 ParseLibraryTree (void)
1294 char toppath
[MAXPATHLEN
+ 1]; /* String holding abs path to top level library dir */
1295 char working
[MAXPATHLEN
+ 1]; /* String holding abs path to working dir */
1296 char *libpaths
; /* String holding list of library paths to search */
1297 char *p
; /* Helper string used in iteration */
1298 DIR *dirobj
; /* Iterable directory object */
1299 struct dirent
*direntry
= NULL
; /* Object holding individual directory entries */
1300 struct stat buffer
; /* buffer used in stat */
1301 int n_footprints
= 0; /* Running count of footprints found */
1303 /* Initialize path, working by writing 0 into every byte. */
1304 memset (toppath
, 0, sizeof toppath
);
1305 memset (working
, 0, sizeof working
);
1307 /* Save the current working directory as an absolute path.
1308 * This fcn writes the abs path into the memory pointed to by the input arg.
1310 if (GetWorkingDirectory (working
) == NULL
)
1312 Message (_("ParseLibraryTree: Could not determine initial working directory\n"));
1316 /* Additional loop to allow for multiple 'newlib' style library directories
1317 * called out in Settings.LibraryTree
1319 libpaths
= strdup (Settings
.LibraryTree
);
1320 for (p
= strtok (libpaths
, PCB_PATH_DELIMETER
); p
&& *p
; p
= strtok (NULL
, PCB_PATH_DELIMETER
))
1322 /* remove trailing path delimeter */
1323 strncpy (toppath
, p
, sizeof (toppath
) - 1);
1325 /* start out in the working directory in case the path is a
1328 if (chdir (working
))
1330 ChdirErrorMessage (working
);
1336 * Next change to the directory which is the top of the library tree
1337 * and extract its abs path.
1339 if (chdir (toppath
))
1341 ChdirErrorMessage (toppath
);
1345 if (GetWorkingDirectory (toppath
) == NULL
)
1347 Message (_("ParseLibraryTree: Could not determine new working directory\n"));
1352 printf("In ParseLibraryTree, looking for newlib footprints inside top level directory %s ... \n",
1356 /* Next read in any footprints in the top level dir */
1357 n_footprints
+= LoadNewlibFootprintsFromDir("(local)", toppath
);
1359 /* Then open this dir so we can loop over its contents. */
1360 if ((dirobj
= opendir (toppath
)) == NULL
)
1362 OpendirErrorMessage (toppath
);
1366 /* Now loop over files in this directory looking for subdirs.
1367 * For each direntry which is a valid subdirectory,
1368 * try to load newlib footprints inside it.
1370 while ((direntry
= readdir (dirobj
)) != NULL
)
1373 printf("In ParseLibraryTree loop examining 2nd level direntry %s ... \n", direntry
->d_name
);
1375 /* Find subdirectories. Ignore entries beginning with "." and CVS
1378 if (!stat (direntry
->d_name
, &buffer
)
1379 && S_ISDIR (buffer
.st_mode
)
1380 && direntry
->d_name
[0] != '.'
1381 && NSTRCMP (direntry
->d_name
, "CVS") != 0)
1383 /* Found a valid subdirectory. Try to load footprints from it.
1385 n_footprints
+= LoadNewlibFootprintsFromDir(direntry
->d_name
, toppath
);
1391 /* restore the original working directory */
1392 if (chdir (working
))
1393 ChdirErrorMessage (working
);
1396 printf("Leaving ParseLibraryTree, found %d footprints.\n", n_footprints
);
1400 return n_footprints
;
1403 /* ---------------------------------------------------------------------------
1404 * Read contents of the library description file (for M4)
1405 * and then read in M4 libs. Then call a fcn to read the newlib
1409 ReadLibraryContents (void)
1411 static char *command
= NULL
;
1412 char inputline
[MAX_LIBRARY_LINE_LENGTH
+ 1];
1413 FILE *resultFP
= NULL
;
1414 LibraryMenuType
*menu
= NULL
;
1415 LibraryEntryType
*entry
;
1417 /* If we don't have a command to execute to find the library contents,
1418 * skip this. This is used by default on Windows builds (set in main.c),
1419 * as we can't normally run shell scripts or expect to have m4 present.
1421 if (Settings
.LibraryContentsCommand
!= NULL
&&
1422 Settings
.LibraryContentsCommand
[0] != '\0')
1424 /* First load the M4 stuff. The variable Settings.LibraryPath
1428 command
= EvaluateFilename (Settings
.LibraryContentsCommand
,
1429 Settings
.LibraryPath
, Settings
.LibraryFilename
,
1433 printf("In ReadLibraryContents, about to execute command %s\n", command
);
1436 /* This uses a pipe to execute a shell script which provides the names of
1437 * all M4 libs and footprints. The results are placed in resultFP.
1439 if (command
&& *command
&& (resultFP
= popen (command
, "r")) == NULL
)
1441 PopenErrorMessage (command
);
1444 /* the M4 library contents are separated by colons;
1445 * template : package : name : description
1447 while (resultFP
!= NULL
&& fgets (inputline
, MAX_LIBRARY_LINE_LENGTH
, resultFP
))
1449 size_t len
= strlen (inputline
);
1451 /* check for maximum linelength */
1455 if (inputline
[len
] != '\n')
1457 ("linelength (%i) exceeded; following characters will be ignored\n",
1458 MAX_LIBRARY_LINE_LENGTH
);
1460 inputline
[len
] = '\0';
1463 /* if the line defines a menu */
1464 if (!strncmp (inputline
, "TYPE=", 5))
1466 menu
= GetLibraryMenuMemory (&Library
);
1467 menu
->Name
= strdup (UNKNOWN (&inputline
[5]));
1468 menu
->directory
= strdup (Settings
.LibraryFilename
);
1472 /* allocate a new menu entry if not already done */
1475 menu
= GetLibraryMenuMemory (&Library
);
1476 menu
->Name
= strdup (UNKNOWN ((char *) NULL
));
1477 menu
->directory
= strdup (Settings
.LibraryFilename
);
1479 entry
= GetLibraryEntryMemory (menu
);
1480 entry
->AllocatedMemory
= strdup (inputline
);
1482 /* now break the line into pieces separated by colons */
1483 if ((entry
->Template
= strtok (entry
->AllocatedMemory
, ":")) !=
1485 if ((entry
->Package
= strtok (NULL
, ":")) != NULL
)
1486 if ((entry
->Value
= strtok (NULL
, ":")) != NULL
)
1487 entry
->Description
= strtok (NULL
, ":");
1489 /* create the list entry */
1490 len
= strlen (EMPTY (entry
->Value
)) +
1491 strlen (EMPTY (entry
->Description
)) + 4;
1492 entry
->ListEntry
= (char *)calloc (len
, sizeof (char));
1493 sprintf (entry
->ListEntry
,
1494 "%s, %s", EMPTY (entry
->Value
),
1495 EMPTY (entry
->Description
));
1498 if (resultFP
!= NULL
)
1502 /* Now after reading in the M4 libs, call a function to
1503 * read the newlib footprint libraries. Then sort the whole
1506 if (ParseLibraryTree () > 0 || resultFP
!= NULL
)
1508 sort_library (&Library
);
1515 #define BLANK(x) ((x) == ' ' || (x) == '\t' || (x) == '\n' \
1518 /* ---------------------------------------------------------------------------
1519 * Read in a netlist and store it in the netlist menu
1523 ReadNetlist (char *filename
)
1525 static char *command
= NULL
;
1526 char inputline
[MAX_NETLIST_LINE_LENGTH
+ 1];
1527 char temp
[MAX_NETLIST_LINE_LENGTH
+ 1];
1529 LibraryMenuType
*menu
= NULL
;
1530 LibraryEntryType
*entry
;
1531 int i
, j
, lines
, kind
;
1533 bool used_popen
= false;
1537 return 1; /* nothing to do */
1539 Message (_("Importing PCB netlist %s\n"), filename
);
1541 if (EMPTY_STRING_P (Settings
.RatCommand
))
1543 fp
= fopen (filename
, "r");
1546 Message("Cannot open %s for reading", filename
);
1554 command
= EvaluateFilename (Settings
.RatCommand
,
1555 Settings
.RatPath
, filename
, NULL
);
1557 /* open pipe to stdout of command */
1558 if (*command
== '\0' || (fp
= popen (command
, "r")) == NULL
)
1560 PopenErrorMessage (command
);
1565 /* kind = 0 is net name
1566 * kind = 1 is route style name
1567 * kind = 2 is connection
1570 while (fgets (inputline
, MAX_NETLIST_LINE_LENGTH
, fp
))
1572 size_t len
= strlen (inputline
);
1573 /* check for maximum length line */
1576 if (inputline
[--len
] != '\n')
1577 Message (_("Line length (%i) exceeded in netlist file.\n"
1578 "additional characters will be ignored.\n"),
1579 MAX_NETLIST_LINE_LENGTH
);
1581 inputline
[len
] = '\0';
1583 continued
= (inputline
[len
- 1] == '\\') ? true : false;
1585 inputline
[len
- 1] = '\0';
1588 while (inputline
[i
] != '\0')
1591 /* skip leading blanks */
1592 while (inputline
[i
] != '\0' && BLANK (inputline
[i
]))
1596 /* add two spaces for included/unincluded */
1600 while (!BLANK (inputline
[i
]))
1601 temp
[j
++] = inputline
[i
++];
1603 while (inputline
[i
] != '\0' && BLANK (inputline
[i
]))
1607 menu
= GetLibraryMenuMemory (&PCB
->NetlistLib
);
1608 menu
->Name
= strdup (temp
);
1614 if (kind
== 1 && strchr (temp
, '-') == NULL
)
1617 menu
->Style
= strdup (temp
);
1621 entry
= GetLibraryEntryMemory (menu
);
1622 entry
->ListEntry
= strdup (temp
);
1631 Message (_("Empty netlist file!\n"));
1642 static int ReadEdifNetlist (char *filename
);
1644 int ImportNetlist (char *filename
)
1652 if (!filename
) return (1); /* nothing to do */
1653 fp
= fopen (filename
, "r");
1654 if (!fp
) return (1); /* bad filename */
1655 i
= fread (buf
, 1, sizeof(buf
)-1, fp
);
1661 *p
= tolower ((int) *p
);
1664 p
= strstr (buf
, "edif");
1665 if (!p
) return ReadNetlist (filename
);
1666 else return ReadEdifNetlist (filename
);
1669 static int ReadEdifNetlist (char *filename
)
1671 Message (_("Importing edif netlist %s\n"), filename
);
1672 ParseEDIF(filename
, NULL
);