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 *, Boolean
);
126 static int ParseLibraryTree (void);
128 /* ---------------------------------------------------------------------------
129 * Flag helper functions
132 #define F2S(OBJ, TYPE) flags_to_string ((OBJ)->Flags, TYPE)
134 /* --------------------------------------------------------------------------- */
137 string_cmp (const char *a
, const char *b
)
141 if (isdigit ((int) *a
) && isdigit ((int) *b
))
147 while (isdigit ((int) *a
))
149 while (isdigit ((int) *b
))
152 else if (tolower ((int) *a
) != tolower ((int) *b
))
153 return tolower ((int) *a
) - tolower ((int) *b
);
164 static int netlist_sort_offset
= 0;
167 netlist_sort (const void *va
, const void *vb
)
169 LibraryMenuType
*am
= (LibraryMenuType
*) va
;
170 LibraryMenuType
*bm
= (LibraryMenuType
*) vb
;
177 return string_cmp (a
, b
);
181 netnode_sort (const void *va
, const void *vb
)
183 LibraryEntryType
*am
= (LibraryEntryType
*) va
;
184 LibraryEntryType
*bm
= (LibraryEntryType
*) vb
;
185 char *a
= am
->ListEntry
;
186 char *b
= bm
->ListEntry
;
187 return string_cmp (a
, b
);
191 sort_library (LibraryTypePtr lib
)
194 qsort (lib
->Menu
, lib
->MenuN
, sizeof (lib
->Menu
[0]), netlist_sort
);
195 for (i
= 0; i
< lib
->MenuN
; i
++)
196 qsort (lib
->Menu
[i
].Entry
,
197 lib
->Menu
[i
].EntryN
, sizeof (lib
->Menu
[i
].Entry
[0]), netnode_sort
);
203 netlist_sort_offset
= 2;
204 sort_library (&(PCB
->NetlistLib
));
205 netlist_sort_offset
= 0;
208 /* ---------------------------------------------------------------------------
209 * opens a file and check if it exists
212 CheckAndOpenFile (char *Filename
, Boolean Confirm
, Boolean AllButton
,
213 Boolean
* WasAllButton
, Boolean
* WasCancelButton
)
217 char message
[MAXPATHLEN
+ 80];
220 if (Filename
&& *Filename
)
222 if (!stat (Filename
, &buffer
) && Confirm
)
224 sprintf (message
, _("File '%s' exists, use anyway?"), Filename
);
226 *WasAllButton
= False
;
228 *WasCancelButton
= False
;
231 gui
->confirm_dialog (message
, "Cancel", "Ok",
232 AllButton
? "Sequence OK" : 0);
235 gui
->confirm_dialog (message
, "Cancel", "Ok", "Sequence OK");
241 *WasAllButton
= True
;
245 *WasCancelButton
= True
;
248 if ((fp
= fopen (Filename
, "w")) == NULL
)
249 OpenErrorMessage (Filename
);
254 /* ---------------------------------------------------------------------------
255 * opens a file for saving connection data
258 OpenConnectionDataFile (void)
262 static char * default_file
= NULL
;
263 Boolean result
; /* not used */
265 /* CheckAndOpenFile deals with the case where fname already exists */
266 fname
= gui
->fileselect (_("Save Connection Data As ..."),
267 _("Choose a file to save all connection data to."),
268 default_file
, ".net", "connection_data",
273 if (default_file
!= NULL
)
280 default_file
= strdup (fname
);
282 fp
= CheckAndOpenFile (fname
, True
, False
, &result
, NULL
);
288 /* ---------------------------------------------------------------------------
289 * save elements in the current buffer
292 SaveBufferElements (char *Filename
)
298 result
= WritePipe (Filename
, False
);
304 /* ---------------------------------------------------------------------------
308 SavePCB (char *Filename
)
313 if (!(retcode
= WritePipe (Filename
, True
)))
315 /* thanks to Nick Bailey for the bug-fix;
316 * first of all make a copy of the passed filename because
317 * it might be identical to 'PCB->Filename'
319 copy
= MyStrdup (Filename
, "SavePCB()");
320 SaveFree (PCB
->Filename
);
321 PCB
->Filename
= copy
;
322 SetChangedFlag (False
);
327 /* ---------------------------------------------------------------------------
328 * set the route style to the first one, if the current one doesn't
329 * happen to match any. This way, "revert" won't change the route
333 set_some_route_style ()
335 if (hid_get_flag ("style"))
337 SetLineSize (PCB
->RouteStyle
[0].Thick
);
338 SetViaSize (PCB
->RouteStyle
[0].Diameter
, True
);
339 SetViaDrillingHole (PCB
->RouteStyle
[0].Hole
, True
);
340 SetKeepawayWidth (PCB
->RouteStyle
[0].Keepaway
);
343 /* ---------------------------------------------------------------------------
345 * parse the file with enabled 'PCB mode' (see parser)
346 * if successful, update some other stuff
349 LoadPCB (char *Filename
)
351 PCBTypePtr newPCB
= CreateNewPCB (False
);
354 /* new data isn't added to the undo list */
355 if (!ParsePCB (newPCB
, Filename
))
360 CreateNewPCBPost (PCB
, 0);
361 ResetStackAndVisibility ();
363 /* update cursor location */
364 Crosshair
.X
= MAX (0, MIN (PCB
->CursorX
, (LocationType
) PCB
->MaxWidth
));
366 MAX (0, MIN (PCB
->CursorY
, (LocationType
) PCB
->MaxHeight
));
368 Xorig
= Crosshair
.X
- TO_PCB (Output
.Width
/ 2);
369 Yorig
= Crosshair
.Y
- TO_PCB (Output
.Height
/ 2);
371 /* update cursor confinement and output area (scrollbars) */
372 ChangePCBSize (PCB
->MaxWidth
, PCB
->MaxHeight
);
374 /* create default font if necessary */
375 if (!PCB
->Font
.Valid
)
378 ("File '%s' has no font information, using default font\n"),
380 CreateDefaultFont ();
383 /* clear 'changed flag' */
384 SetChangedFlag (False
);
385 PCB
->Filename
= MyStrdup (Filename
, "LoadPCB()");
386 /* just in case a bad file saved file is loaded */
388 units_mm
= (PCB
->Grid
!= (int) PCB
->Grid
) ? True
: False
;
390 Settings
.grid_units_mm
= units_mm
;
394 set_some_route_style ();
396 hid_action ("PCBChanged");
400 hid_action ("PCBChanged");
402 /* release unused memory */
407 /* ---------------------------------------------------------------------------
408 * functions for loading elements-as-pcb
411 extern PCBTypePtr yyPCB
;
412 extern DataTypePtr yyData
;
413 extern FontTypePtr yyFont
;
422 yyFont
= &yyPCB
->Font
;
423 yyData
= yyPCB
->Data
;
424 yyData
->pcb
= (void *)yyPCB
;
429 PostLoadElementPCB ()
431 PCBTypePtr pcb_save
= PCB
;
437 CreateNewPCBPost (yyPCB
, 0);
438 ParseGroupString("1,c:2,s", &yyPCB
->LayerGroups
, yyData
->LayerN
);
439 e
= yyPCB
->Data
->Element
; /* we know there's only one */
441 MoveElementLowLevel (yyPCB
->Data
,
442 e
, -e
->BoundingBox
.X1
, -e
->BoundingBox
.Y1
);
444 yyPCB
->MaxWidth
= e
->BoundingBox
.X2
;
445 yyPCB
->MaxHeight
= e
->BoundingBox
.Y2
;
448 /* ---------------------------------------------------------------------------
449 * writes the quoted string created by another subroutine
452 PrintQuotedString (FILE * FP
, char *S
)
454 static DynamicStringType ds
;
456 CreateQuotedString (&ds
, S
);
460 /* ---------------------------------------------------------------------------
461 * writes out an attribute list
464 WriteAttributeList (FILE * FP
, AttributeListTypePtr list
, char *prefix
)
468 for (i
= 0; i
< list
->Number
; i
++)
469 fprintf (FP
, "%sAttribute(\"%s\" \"%s\")\n",
470 prefix
, list
->List
[i
].name
, list
->List
[i
].value
);
473 /* ---------------------------------------------------------------------------
474 * writes layout header information
475 * date, UID and name of user
478 WritePCBInfoHeader (FILE * FP
)
481 struct passwd
*pwentry
;
484 #ifdef HAVE_GETHOSTNAME
485 static struct hostent
*hostentry
= NULL
;
490 /* write some useful comments */
491 currenttime
= time (NULL
);
492 fprintf (FP
, "# release: %s " VERSION
"\n", Progname
);
493 fprintf (FP
, "# date: %s", asctime (localtime (¤ttime
)));
496 pwentry
= getpwuid (getuid ());
497 fprintf (FP
, "# user: %s (%s)\n", pwentry
->pw_name
, pwentry
->pw_gecos
);
499 fprintf (FP
, "# user: Unknown\n");
502 #ifdef HAVE_GETHOSTNAME
503 if (gethostname (hostname
, 255) != -1)
505 if (hostentry
== NULL
)
506 hostentry
= gethostbyname (hostname
);
507 fprintf (FP
, "# host: %s\n",
508 hostentry
? hostentry
->h_name
: hostname
);
511 fprintf (FP
, "# host: Unknown\n");
515 /* ---------------------------------------------------------------------------
517 * the name of the PCB, cursor location, zoom and grid
518 * layergroups and some flags
521 WritePCBDataHeader (FILE * FP
)
526 * ************************** README *******************
527 * ************************** README *******************
529 * If the file format is modified in any way, update
530 * PCB_FILE_VERSION in file.h
532 * ************************** README *******************
533 * ************************** README *******************
536 fprintf (FP
, "\n# To read pcb files, the pcb version (or the cvs source date) must be >= the file version\n");
537 fprintf (FP
, "FileVersion[%i]\n", PCB_FILE_VERSION
);
539 fputs ("\nPCB[", FP
);
540 PrintQuotedString (FP
, EMPTY (PCB
->Name
));
541 fprintf (FP
, " %i %i]\n\n", (int) PCB
->MaxWidth
, (int) PCB
->MaxHeight
);
542 fprintf (FP
, "Grid[%s %i %i %i]\n",
543 c_dtostr (PCB
->Grid
), (int) PCB
->GridOffsetX
,
544 (int) PCB
->GridOffsetY
, (int) Settings
.DrawGrid
);
545 fprintf (FP
, "Cursor[%i %i %s]\n", (int) TO_PCB_X (Output
.Width
/ 2),
546 (int) TO_PCB_Y (Output
.Height
/ 2), c_dtostr (PCB
->Zoom
));
547 fprintf (FP
, "PolyArea[%s]\n", c_dtostr (PCB
->IsleArea
));
548 fprintf (FP
, "Thermal[%s]\n", c_dtostr (PCB
->ThermScale
));
549 fprintf (FP
, "DRC[%i %i %i %i %i %i]\n", PCB
->Bloat
, PCB
->Shrink
,
550 PCB
->minWid
, PCB
->minSlk
, PCB
->minDrill
, PCB
->minRing
);
551 fprintf (FP
, "Flags(%s)\n", pcbflags_to_string(PCB
->Flags
));
552 fprintf (FP
, "Groups(\"%s\")\n", LayerGroupsToString (&PCB
->LayerGroups
));
553 fputs ("Styles[\"", FP
);
554 for (group
= 0; group
< NUM_STYLES
- 1; group
++)
555 fprintf (FP
, "%s,%i,%i,%i,%i:", PCB
->RouteStyle
[group
].Name
,
556 PCB
->RouteStyle
[group
].Thick
,
557 PCB
->RouteStyle
[group
].Diameter
,
558 PCB
->RouteStyle
[group
].Hole
, PCB
->RouteStyle
[group
].Keepaway
);
559 fprintf (FP
, "%s,%i,%i,%i,%i\"]\n\n", PCB
->RouteStyle
[group
].Name
,
560 PCB
->RouteStyle
[group
].Thick
,
561 PCB
->RouteStyle
[group
].Diameter
,
562 PCB
->RouteStyle
[group
].Hole
, PCB
->RouteStyle
[group
].Keepaway
);
565 /* ---------------------------------------------------------------------------
566 * writes font data of non empty symbols
569 WritePCBFontData (FILE * FP
)
575 for (font
= &PCB
->Font
, i
= 0; i
<= MAX_FONTPOSITION
; i
++)
577 if (!font
->Symbol
[i
].Valid
)
580 if (isprint (i
) && font
->Symbol
[i
].Delta
% 100 == 0)
581 fprintf (FP
, "Symbol('%c' %i)\n(\n",
582 (char) i
, (int) font
->Symbol
[i
].Delta
/ 100);
583 else if (isprint (i
))
584 fprintf (FP
, "Symbol['%c' %i]\n(\n",
585 (char) i
, (int) font
->Symbol
[i
].Delta
);
587 fprintf (FP
, "Symbol[%i %i]\n(\n", i
, (int) font
->Symbol
[i
].Delta
);
589 line
= font
->Symbol
[i
].Line
;
590 for (j
= font
->Symbol
[i
].LineN
; j
; j
--, line
++)
592 if (line
->Point1
.X
% 100 == 0
593 && line
->Point1
.Y
% 100 == 0
594 && line
->Point2
.X
% 100 == 0
595 && line
->Point2
.Y
% 100 == 0 && line
->Thickness
% 100 == 0)
596 fprintf (FP
, "\tSymbolLine(%i %i %i %i %i)\n",
597 line
->Point1
.X
/ 100, line
->Point1
.Y
/ 100,
598 line
->Point2
.X
/ 100, line
->Point2
.Y
/ 100,
599 line
->Thickness
/ 100);
601 fprintf (FP
, "\tSymbolLine[%i %i %i %i %i]\n",
602 line
->Point1
.X
, line
->Point1
.Y
,
603 line
->Point2
.X
, line
->Point2
.Y
, line
->Thickness
);
609 /* ---------------------------------------------------------------------------
613 WriteViaData (FILE * FP
, DataTypePtr Data
)
616 /* write information about vias */
617 for (n
= 0; n
< Data
->ViaN
; n
++)
619 PinTypePtr via
= &Data
->Via
[n
];
620 fprintf (FP
, "Via[%i %i %i %i %i %i ",
622 via
->Thickness
, via
->Clearance
, via
->Mask
, via
->DrillingHole
);
623 PrintQuotedString (FP
, EMPTY (via
->Name
));
624 fprintf (FP
, " %s]\n", F2S (via
, VIA_TYPE
));
628 /* ---------------------------------------------------------------------------
629 * writes rat-line data
632 WritePCBRatData (FILE * FP
)
635 /* write information about rats */
636 for (n
= 0; n
< PCB
->Data
->RatN
; n
++)
638 RatTypePtr line
= &PCB
->Data
->Rat
[n
];
639 fprintf (FP
, "Rat[%i %i %i %i %i %i ",
640 (int) line
->Point1
.X
, (int) line
->Point1
.Y
,
641 (int) line
->group1
, (int) line
->Point2
.X
,
642 (int) line
->Point2
.Y
, (int) line
->group2
);
643 fprintf (FP
, " %s]\n", F2S (line
, RATLINE_TYPE
));
647 /* ---------------------------------------------------------------------------
648 * writes netlist data
651 WritePCBNetlistData (FILE * FP
)
653 /* write out the netlist if it exists */
654 if (PCB
->NetlistLib
.MenuN
)
657 fprintf (FP
, "NetList()\n(\n");
659 for (n
= 0; n
< PCB
->NetlistLib
.MenuN
; n
++)
661 LibraryMenuTypePtr menu
= &PCB
->NetlistLib
.Menu
[n
];
662 fprintf (FP
, "\tNet(");
663 PrintQuotedString(FP
, &menu
->Name
[2]);
665 PrintQuotedString(FP
, UNKNOWN (menu
->Style
));
666 fprintf (FP
, ")\n\t(\n");
667 for (p
= 0; p
< menu
->EntryN
; p
++)
669 LibraryEntryTypePtr entry
= &menu
->Entry
[p
];
670 fprintf (FP
, "\t\tConnect(");
671 PrintQuotedString (FP
, entry
->ListEntry
);
674 fprintf (FP
, "\t)\n");
680 /* ---------------------------------------------------------------------------
681 * writes element data
684 WriteElementData (FILE * FP
, DataTypePtr Data
)
687 for (n
= 0; n
< Data
->ElementN
; n
++)
689 ElementTypePtr element
= &Data
->Element
[n
];
690 /* only non empty elements */
691 if (!element
->LineN
&& !element
->PinN
&& !element
->ArcN
694 /* the coordinates and text-flags are the same for
695 * both names of an element
697 fprintf (FP
, "\nElement[%s ", F2S (element
, ELEMENT_TYPE
));
698 PrintQuotedString (FP
, EMPTY (DESCRIPTION_NAME (element
)));
700 PrintQuotedString (FP
, EMPTY (NAMEONPCB_NAME (element
)));
702 PrintQuotedString (FP
, EMPTY (VALUE_NAME (element
)));
703 fprintf (FP
, " %i %i %i %i %i %i %s]\n(\n",
704 (int) element
->MarkX
, (int) element
->MarkY
,
705 (int) (DESCRIPTION_TEXT (element
).X
-
707 (int) (DESCRIPTION_TEXT (element
).Y
-
709 (int) DESCRIPTION_TEXT (element
).Direction
,
710 (int) DESCRIPTION_TEXT (element
).Scale
,
711 F2S (&(DESCRIPTION_TEXT (element
)), ELEMENTNAME_TYPE
));
712 WriteAttributeList (FP
, &element
->Attributes
, "\t");
713 for (p
= 0; p
< element
->PinN
; p
++)
715 PinTypePtr pin
= &element
->Pin
[p
];
716 fprintf (FP
, "\tPin[%i %i %i %i %i %i ",
717 (int) (pin
->X
- element
->MarkX
),
718 (int) (pin
->Y
- element
->MarkY
),
719 (int) pin
->Thickness
, (int) pin
->Clearance
,
720 (int) pin
->Mask
, (int) pin
->DrillingHole
);
721 PrintQuotedString (FP
, EMPTY (pin
->Name
));
723 PrintQuotedString (FP
, EMPTY (pin
->Number
));
724 fprintf (FP
, " %s]\n", F2S (pin
, PIN_TYPE
));
726 for (p
= 0; p
< element
->PadN
; p
++)
728 PadTypePtr pad
= &element
->Pad
[p
];
729 fprintf (FP
, "\tPad[%i %i %i %i %i %i %i ",
730 (int) (pad
->Point1
.X
- element
->MarkX
),
731 (int) (pad
->Point1
.Y
- element
->MarkY
),
732 (int) (pad
->Point2
.X
- element
->MarkX
),
733 (int) (pad
->Point2
.Y
- element
->MarkY
),
734 (int) pad
->Thickness
, (int) pad
->Clearance
,
736 PrintQuotedString (FP
, EMPTY (pad
->Name
));
738 PrintQuotedString (FP
, EMPTY (pad
->Number
));
739 fprintf (FP
, " %s]\n", F2S (pad
, PAD_TYPE
));
741 for (p
= 0; p
< element
->LineN
; p
++)
743 LineTypePtr line
= &element
->Line
[p
];
745 "\tElementLine [%i %i %i %i %i]\n",
746 (int) (line
->Point1
.X
-
748 (int) (line
->Point1
.Y
-
750 (int) (line
->Point2
.X
-
752 (int) (line
->Point2
.Y
-
753 element
->MarkY
), (int) line
->Thickness
);
755 for (p
= 0; p
< element
->ArcN
; p
++)
757 ArcTypePtr arc
= &element
->Arc
[p
];
759 "\tElementArc [%i %i %i %i %i %i %i]\n",
760 (int) (arc
->X
- element
->MarkX
),
761 (int) (arc
->Y
- element
->MarkY
),
762 (int) arc
->Width
, (int) arc
->Height
,
763 (int) arc
->StartAngle
, (int) arc
->Delta
,
764 (int) arc
->Thickness
);
766 fputs ("\n\t)\n", FP
);
770 /* ---------------------------------------------------------------------------
774 WriteLayerData (FILE * FP
, Cardinal Number
, LayerTypePtr layer
)
777 /* write information about non empty layers */
778 if (layer
->LineN
|| layer
->ArcN
|| layer
->TextN
|| layer
->PolygonN
||
779 (layer
->Name
&& *layer
->Name
))
781 fprintf (FP
, "Layer(%i ", (int) Number
+ 1);
782 PrintQuotedString (FP
, EMPTY (layer
->Name
));
783 fputs (")\n(\n", FP
);
784 WriteAttributeList (FP
, &layer
->Attributes
, "\t");
786 for (n
= 0; n
< layer
->LineN
; n
++)
788 LineTypePtr line
= &layer
->Line
[n
];
789 fprintf (FP
, "\tLine[%i %i %i %i %i %i %s]\n",
790 (int) line
->Point1
.X
, (int) line
->Point1
.Y
,
791 (int) line
->Point2
.X
, (int) line
->Point2
.Y
,
792 (int) line
->Thickness
, (int) line
->Clearance
,
793 F2S (line
, LINE_TYPE
));
795 for (n
= 0; n
< layer
->ArcN
; n
++)
797 ArcTypePtr arc
= &layer
->Arc
[n
];
798 fprintf (FP
, "\tArc[%i %i %i %i %i %i %i %i %s]\n",
799 (int) arc
->X
, (int) arc
->Y
, (int) arc
->Width
,
800 (int) arc
->Height
, (int) arc
->Thickness
,
801 (int) arc
->Clearance
, (int) arc
->StartAngle
,
802 (int) arc
->Delta
, F2S (arc
, ARC_TYPE
));
804 for (n
= 0; n
< layer
->TextN
; n
++)
806 TextTypePtr text
= &layer
->Text
[n
];
807 fprintf (FP
, "\tText[%i %i %i %i ",
808 (int) text
->X
, (int) text
->Y
,
809 (int) text
->Direction
, (int) text
->Scale
);
810 PrintQuotedString (FP
, EMPTY (text
->TextString
));
811 fprintf (FP
, " %s]\n", F2S (text
, TEXT_TYPE
));
813 for (n
= 0; n
< layer
->PolygonN
; n
++)
815 PolygonTypePtr polygon
= &layer
->Polygon
[n
];
817 fprintf (FP
, "\tPolygon(%s)\n\t(", F2S (polygon
, POLYGON_TYPE
));
818 for (p
= 0; p
< polygon
->PointN
; p
++)
820 PointTypePtr point
= &polygon
->Points
[p
];
822 fputs ("\n\t\t", FP
);
823 fprintf (FP
, "[%i %i] ", (int) point
->X
, (int) point
->Y
);
825 fputs ("\n\t)\n", FP
);
831 /* ---------------------------------------------------------------------------
832 * writes just the elements in the buffer to file
835 WriteBuffer (FILE * FP
)
839 WriteViaData (FP
, PASTEBUFFER
->Data
);
840 WriteElementData (FP
, PASTEBUFFER
->Data
);
841 for (i
= 0; i
< max_layer
+ 2; i
++)
842 WriteLayerData (FP
, i
, &(PASTEBUFFER
->Data
->Layer
[i
]));
846 /* ---------------------------------------------------------------------------
854 WritePCBInfoHeader (FP
);
855 WritePCBDataHeader (FP
);
856 WritePCBFontData (FP
);
857 WriteAttributeList (FP
, &PCB
->Attributes
, "");
858 WriteViaData (FP
, PCB
->Data
);
859 WriteElementData (FP
, PCB
->Data
);
860 WritePCBRatData (FP
);
861 for (i
= 0; i
< max_layer
+ 2; i
++)
862 WriteLayerData (FP
, i
, &(PCB
->Data
->Layer
[i
]));
863 WritePCBNetlistData (FP
);
868 /* ---------------------------------------------------------------------------
872 WritePCBFile (char *Filename
)
877 if ((fp
= fopen (Filename
, "w")) == NULL
)
879 OpenErrorMessage (Filename
);
880 return (STATUS_ERROR
);
882 result
= WritePCB (fp
);
887 /* ---------------------------------------------------------------------------
888 * writes to pipe using the command defined by Settings.SaveCommand
889 * %f are replaced by the passed filename
892 WritePipe (char *Filename
, Boolean thePcb
)
897 static DynamicStringType command
;
900 if (EMPTY_STRING_P (Settings
.SaveCommand
))
902 fp
= fopen (Filename
, "w");
905 Message ("Unable to write to file %s\n", Filename
);
912 /* setup commandline */
913 DSClearString (&command
);
914 for (p
= Settings
.SaveCommand
; *p
; p
++)
916 /* copy character if not special or add string to command */
917 if (!(*p
== '%' && *(p
+ 1) == 'f'))
918 DSAddCharacter (&command
, *p
);
921 DSAddString (&command
, Filename
);
923 /* skip the character */
927 DSAddCharacter (&command
, '\0');
928 printf ("write to pipe \"%s\"\n", command
.Data
);
929 if ((fp
= popen (command
.Data
, "w")) == NULL
)
931 PopenErrorMessage (command
.Data
);
932 return (STATUS_ERROR
);
936 result
= WritePCB (fp
);
938 result
= WriteBuffer (fp
);
941 return (pclose (fp
) ? STATUS_ERROR
: result
);
942 return (fclose (fp
) ? STATUS_ERROR
: result
);
945 /* ---------------------------------------------------------------------------
946 * saves the layout in a temporary file
947 * this is used for fatal errors and does not call the program specified
948 * in 'saveCommand' for safety reasons
955 /* memory might have been released before this function is called */
956 if (PCB
&& PCB
->Changed
)
958 sprintf (filename
, EMERGENCY_NAME
, (int) getpid ());
959 Message (_("Trying to save your layout in '%s'\n"), filename
);
960 WritePCBFile (filename
);
964 /* ---------------------------------------------------------------------------
965 * front-end for 'SaveInTMP()'
966 * just makes sure that the routine is only called once
968 static Boolean dont_save_any_more
= False
;
973 if (!dont_save_any_more
)
976 dont_save_any_more
= True
;
981 DisableEmergencySave (void)
983 dont_save_any_more
= True
;
986 /* ----------------------------------------------------------------------
987 * Callback for the autosave
990 static hidval backup_timer
;
993 * If the backup interval is > 0 then set another timer. Otherwise
994 * we do nothing and it is up to the GUI to call EnableAutosave()
995 * after setting Settings.BackupInterval > 0 again.
998 backup_cb (hidval data
)
1000 backup_timer
.ptr
= NULL
;
1002 if (Settings
.BackupInterval
> 0 && gui
->add_timer
)
1003 backup_timer
= gui
->add_timer (backup_cb
,
1004 1000 * Settings
.BackupInterval
, data
);
1008 EnableAutosave (void)
1014 /* If we already have a timer going, then cancel it out */
1015 if (backup_timer
.ptr
!= NULL
&& gui
->stop_timer
)
1016 gui
->stop_timer (backup_timer
);
1018 backup_timer
.ptr
= NULL
;
1019 /* Start up a new timer */
1020 if (Settings
.BackupInterval
> 0 && gui
->add_timer
)
1021 backup_timer
= gui
->add_timer (backup_cb
,
1022 1000 * Settings
.BackupInterval
,
1026 /* ---------------------------------------------------------------------------
1027 * creates backup file. The default is to use the pcb file name with
1028 * a "-" appended (like "foo.pcb-") and if we don't have a pcb file name
1029 * then use the template in BACKUP_NAME
1034 char *filename
= NULL
;
1036 if( PCB
&& PCB
->Filename
)
1038 filename
= (char *) malloc (sizeof (char) * (strlen (PCB
->Filename
) + 2));
1039 if (filename
== NULL
)
1041 fprintf (stderr
, "Backup(): malloc failed\n");
1044 sprintf (filename
, "%s-", PCB
->Filename
);
1048 /* BACKUP_NAME has %.8i which will be replaced by the process ID */
1049 filename
= (char *) malloc (sizeof (char) * (strlen (BACKUP_NAME
) + 8));
1050 if (filename
== NULL
)
1052 fprintf (stderr
, "Backup(): malloc failed\n");
1055 sprintf (filename
, BACKUP_NAME
, (int) getpid ());
1058 WritePCBFile (filename
);
1062 #if !defined(HAS_ATEXIT) && !defined(HAS_ON_EXIT)
1063 /* ---------------------------------------------------------------------------
1064 * makes a temporary copy of the data. This is useful for systems which
1065 * doesn't support calling functions on exit. We use this to save the data
1066 * before LEX and YACC functions are called because they are able to abort
1072 sprintf (TMPFilename
, EMERGENCY_NAME
, (int) getpid ());
1073 WritePCBFile (TMPFilename
);
1076 /* ---------------------------------------------------------------------------
1077 * removes the temporary copy of the data file
1080 RemoveTMPData (void)
1082 unlink (TMPFilename
);
1086 /* ---------------------------------------------------------------------------
1087 * parse the directory tree where additional library elements are found
1091 pcb_basename (char *p
)
1093 char *rv
= strrchr (p
, '/');
1100 ParseLibraryTree (void)
1102 char path
[MAXPATHLEN
+ 1];
1103 char working
[MAXPATHLEN
+ 1];
1107 struct dirent
*direntry
, *e2
;
1108 LibraryMenuTypePtr menu
= NULL
;
1109 LibraryEntryTypePtr entry
;
1111 int n_footprints
= 0;
1113 memset (path
, 0, sizeof path
);
1114 memset (working
, 0, sizeof working
);
1116 /* save the current working directory */
1117 GetWorkingDirectory (working
);
1119 /* Additional loop to allow for multiple 'newlib' style library directories */
1120 libpaths
= MyStrdup (Settings
.LibraryTree
, "ParseLibraryTree");
1121 for (p
= strtok (libpaths
, PCB_PATH_DELIMETER
); p
&& *p
; p
= strtok (NULL
, PCB_PATH_DELIMETER
))
1123 strncpy (path
, p
, sizeof (path
) - 1);
1126 * start out in the working directory in case the path is a
1131 if ((dir
= opendir (path
)) == NULL
)
1133 OpendirErrorMessage (path
);
1138 * change to the directory which is the top of the library tree
1139 * and then extract that directory to ensure we have a full path
1140 * name, not a relative path name.
1143 GetWorkingDirectory (path
);
1145 /* read all entries */
1146 while ((direntry
= readdir (dir
)) != NULL
)
1150 * ignore entries beginning with "." and CVS
1151 * directories as well as a few other specific
1152 * files. We're skipping .png and .html because those
1153 * may exist in a library tree to provide an html browsable
1154 * index of the library.
1156 if (!stat (direntry
->d_name
, &buffer
)
1157 && S_ISDIR (buffer
.st_mode
) && direntry
->d_name
[0] != '.'
1158 && NSTRCMP (direntry
->d_name
, "CVS") != 0)
1160 /* add directory name into menu */
1161 menu
= GetLibraryMenuMemory (&Library
);
1162 menu
->Name
= MyStrdup (direntry
->d_name
, "ParseLibraryTree()");
1164 menu
->directory
= strdup (pcb_basename (path
));
1165 subdir
= opendir (direntry
->d_name
);
1166 chdir (direntry
->d_name
);
1167 while (subdir
&& (e2
= readdir (subdir
)))
1169 l
= strlen (e2
->d_name
);
1170 if (!stat (e2
->d_name
, &buffer
) && S_ISREG (buffer
.st_mode
)
1171 && e2
->d_name
[0] != '.'
1172 && NSTRCMP (e2
->d_name
, "CVS") != 0
1173 && NSTRCMP (e2
->d_name
, "Makefile") != 0
1174 && NSTRCMP (e2
->d_name
, "Makefile.am") != 0
1175 && NSTRCMP (e2
->d_name
, "Makefile.in") != 0
1176 && (l
< 4 || NSTRCMP(e2
->d_name
+ (l
- 4), ".png") != 0)
1177 && (l
< 5 || NSTRCMP(e2
->d_name
+ (l
- 5), ".html") != 0) )
1181 * Besides the length for the 3 strings listed,
1182 * we have two "/" added in below, and also we
1183 * have 2 '\0' characters since
1184 * entry->AllocatedMemory actually will contain
1185 * 2 null terminated strings. Thats why we
1188 long len
= strlen (path
) + strlen (e2
->d_name
) +
1189 strlen (direntry
->d_name
) + 4;
1192 (e2
->d_name
+ strlen (e2
->d_name
) - 4, ".lel") != 0)
1196 entry
= GetLibraryEntryMemory (menu
);
1197 entry
->AllocatedMemory
= MyCalloc (1, len
,
1198 "ParseLibraryTree()");
1199 strcat (entry
->AllocatedMemory
, path
);
1200 strcat (entry
->AllocatedMemory
, PCB_DIR_SEPARATOR_S
);
1201 strcat (entry
->AllocatedMemory
, direntry
->d_name
);
1202 strcat (entry
->AllocatedMemory
, PCB_DIR_SEPARATOR_S
);
1203 entry
->ListEntry
= entry
->AllocatedMemory
1204 + strlen (entry
->AllocatedMemory
);
1205 strcat (entry
->AllocatedMemory
, e2
->d_name
);
1206 /* mark as directory tree library */
1207 entry
->Template
= (char *) -1;
1217 /* restore the original working directory */
1219 return n_footprints
;
1222 /* ---------------------------------------------------------------------------
1223 * read contents of the library description file
1224 * also parse a special library directory tree
1227 ReadLibraryContents (void)
1229 static char *command
= NULL
;
1230 char inputline
[MAX_LIBRARY_LINE_LENGTH
+ 1];
1231 FILE *resultFP
= NULL
;
1232 LibraryMenuTypePtr menu
= NULL
;
1233 LibraryEntryTypePtr entry
;
1236 command
= EvaluateFilename (Settings
.LibraryContentsCommand
,
1237 Settings
.LibraryPath
, Settings
.LibraryFilename
,
1240 /* open a pipe to the output of the command */
1241 if (command
&& *command
&& (resultFP
= popen (command
, "r")) == NULL
)
1243 PopenErrorMessage (command
);
1246 /* the library contents are separated by colons;
1247 * template : package : name : description
1249 while (resultFP
!= NULL
&& fgets (inputline
, MAX_LIBRARY_LINE_LENGTH
, resultFP
))
1251 size_t len
= strlen (inputline
);
1253 /* check for maximum linelength */
1257 if (inputline
[len
] != '\n')
1259 ("linelength (%i) exceeded; following characters will be ignored\n",
1260 MAX_LIBRARY_LINE_LENGTH
);
1262 inputline
[len
] = '\0';
1265 /* if the line defines a menu */
1266 if (!strncmp (inputline
, "TYPE=", 5))
1268 menu
= GetLibraryMenuMemory (&Library
);
1269 menu
->Name
= MyStrdup (UNKNOWN (&inputline
[5]),
1270 "ReadLibraryDescription()");
1271 menu
->directory
= strdup (Settings
.LibraryFilename
);
1275 /* allocate a new menu entry if not already done */
1278 menu
= GetLibraryMenuMemory (&Library
);
1279 menu
->Name
= MyStrdup (UNKNOWN ((char *) NULL
),
1280 "ReadLibraryDescription()");
1281 menu
->directory
= strdup (Settings
.LibraryFilename
);
1283 entry
= GetLibraryEntryMemory (menu
);
1284 entry
->AllocatedMemory
= MyStrdup (inputline
,
1285 "ReadLibraryDescription()");
1287 /* now break the line into pieces separated by colons */
1288 if ((entry
->Template
= strtok (entry
->AllocatedMemory
, ":")) !=
1290 if ((entry
->Package
= strtok (NULL
, ":")) != NULL
)
1291 if ((entry
->Value
= strtok (NULL
, ":")) != NULL
)
1292 entry
->Description
= strtok (NULL
, ":");
1294 /* create the list entry */
1295 len
= strlen (EMPTY (entry
->Value
)) +
1296 strlen (EMPTY (entry
->Description
)) + 4;
1297 entry
->ListEntry
= MyCalloc (len
, sizeof (char),
1298 "ReadLibraryDescription()");
1299 sprintf (entry
->ListEntry
,
1300 "%s, %s", EMPTY (entry
->Value
),
1301 EMPTY (entry
->Description
));
1304 if (resultFP
!= NULL
)
1307 if (ParseLibraryTree () > 0 || resultFP
!= NULL
)
1309 sort_library (&Library
);
1316 #define BLANK(x) ((x) == ' ' || (x) == '\t' || (x) == '\n' \
1319 /* ---------------------------------------------------------------------------
1320 * Read in a netlist and store it in the netlist menu
1324 ReadNetlist (char *filename
)
1326 static char *command
= NULL
;
1327 char inputline
[MAX_NETLIST_LINE_LENGTH
+ 1];
1328 char temp
[MAX_NETLIST_LINE_LENGTH
+ 1];
1330 LibraryMenuTypePtr menu
= NULL
;
1331 LibraryEntryTypePtr entry
;
1332 int i
, j
, lines
, kind
;
1337 return (1); /* nothing to do */
1339 Message (_("Importing PCB netlist %s\n"), filename
);
1341 if (EMPTY_STRING_P (Settings
.RatCommand
))
1343 fp
= fopen (filename
, "r");
1346 Message("Cannot open %s for reading", filename
);
1354 command
= EvaluateFilename (Settings
.RatCommand
,
1355 Settings
.RatPath
, filename
, NULL
);
1357 /* open pipe to stdout of command */
1358 if (*command
== '\0' || (fp
= popen (command
, "r")) == NULL
)
1360 PopenErrorMessage (command
);
1365 /* kind = 0 is net name
1366 * kind = 1 is route style name
1367 * kind = 2 is connection
1370 while (fgets (inputline
, MAX_NETLIST_LINE_LENGTH
, fp
))
1372 size_t len
= strlen (inputline
);
1373 /* check for maximum length line */
1376 if (inputline
[--len
] != '\n')
1377 Message (_("Line length (%i) exceeded in netlist file.\n"
1378 "additional characters will be ignored.\n"),
1379 MAX_NETLIST_LINE_LENGTH
);
1381 inputline
[len
] = '\0';
1383 continued
= (inputline
[len
- 1] == '\\') ? True
: False
;
1385 inputline
[len
- 1] = '\0';
1388 while (inputline
[i
] != '\0')
1391 /* skip leading blanks */
1392 while (inputline
[i
] != '\0' && BLANK (inputline
[i
]))
1396 /* add two spaces for included/unincluded */
1400 while (!BLANK (inputline
[i
]))
1401 temp
[j
++] = inputline
[i
++];
1403 while (inputline
[i
] != '\0' && BLANK (inputline
[i
]))
1407 menu
= GetLibraryMenuMemory (&PCB
->NetlistLib
);
1408 menu
->Name
= MyStrdup (temp
, "ReadNetlist()");
1414 if (kind
== 1 && strchr (temp
, '-') == NULL
)
1417 menu
->Style
= MyStrdup (temp
, "ReadNetlist()");
1421 entry
= GetLibraryEntryMemory (menu
);
1422 entry
->ListEntry
= MyStrdup (temp
, "ReadNetlist()");
1431 Message (_("Empty netlist file!\n"));
1443 static int ReadEdifNetlist (char *filename
);
1445 int ImportNetlist (char *filename
)
1453 if (!filename
) return (1); /* nothing to do */
1454 fp
= fopen (filename
, "r");
1455 if (!fp
) return (1); /* bad filename */
1456 i
= fread (buf
, 1, sizeof(buf
)-1, fp
);
1462 *p
= tolower ((int) *p
);
1465 p
= strstr (buf
, "edif");
1466 if (!p
) return ReadNetlist (filename
);
1467 else return ReadEdifNetlist (filename
);
1470 static int ReadEdifNetlist (char *filename
)
1472 Message (_("Importing edif netlist %s\n"), filename
);
1473 ParseEDIF(filename
, NULL
);