examples: Add ".pcb" extension to "PCB(2)", move "LED.NET" to "LED.net"
[geda-pcb/gde.git] / src / file.c
blobdb167c8a8229ab846135a2a295109e9023f5bf55
1 /* $Id$ */
3 /*
4 * COPYRIGHT
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
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
38 #ifdef HAVE_SYS_PARAM_H
39 #include <sys/param.h>
40 #endif
42 #include "global.h"
44 #include <dirent.h>
45 #ifdef HAVE_PWD_H
46 #include <pwd.h>
47 #endif
48 #include <time.h>
50 #ifdef HAVE_SYS_SOCKET_H
51 #include <sys/socket.h>
52 #endif
54 #include <sys/stat.h>
56 #ifdef HAVE_NETINET_IN_H
57 #include <netinet/in.h>
58 #endif
60 #ifdef HAVE_NETDB_H
61 #include <netdb.h>
62 #endif
64 #include <stdio.h>
66 #ifdef HAVE_STDLIB_H
67 #include <stdlib.h>
68 #endif
70 #ifdef HAVE_STRING_H
71 #include <string.h>
72 #endif
74 #ifdef HAVE_UNISTD_H
75 #include <unistd.h>
76 #endif
79 #include "buffer.h"
80 #include "change.h"
81 #include "create.h"
82 #include "crosshair.h"
83 #include "data.h"
84 #include "edif_parse.h"
85 #include "error.h"
86 #include "file.h"
87 #include "hid.h"
88 #include "misc.h"
89 #include "move.h"
90 #include "mymem.h"
91 #include "parse_l.h"
92 #include "polygon.h"
93 #include "rats.h"
94 #include "remove.h"
95 #include "set.h"
96 #include "strflags.h"
98 #ifdef HAVE_LIBDMALLOC
99 #include <dmalloc.h>
100 #endif
102 RCSID ("$Id$");
104 #if !defined(HAS_ATEXIT) && !defined(HAS_ON_EXIT)
105 /* ---------------------------------------------------------------------------
106 * some local identifiers for OS without an atexit() or on_exit()
107 * call
109 static char TMPFilename[80];
110 #endif
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 /* --------------------------------------------------------------------------- */
136 static int
137 string_cmp (const char *a, const char *b)
139 while (*a && *b)
141 if (isdigit ((int) *a) && isdigit ((int) *b))
143 int ia = atoi (a);
144 int ib = atoi (b);
145 if (ia != ib)
146 return ia - ib;
147 while (isdigit ((int) *a))
148 a++;
149 while (isdigit ((int) *b))
150 b++;
152 else if (tolower ((int) *a) != tolower ((int) *b))
153 return tolower ((int) *a) - tolower ((int) *b);
154 a++;
155 b++;
157 if (*a)
158 return 1;
159 if (*b)
160 return -1;
161 return 0;
164 static int netlist_sort_offset = 0;
166 static int
167 netlist_sort (const void *va, const void *vb)
169 LibraryMenuType *am = (LibraryMenuType *) va;
170 LibraryMenuType *bm = (LibraryMenuType *) vb;
171 char *a = am->Name;
172 char *b = bm->Name;
173 if (*a == '~')
174 a++;
175 if (*b == '~')
176 b++;
177 return string_cmp (a, b);
180 static int
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);
190 static void
191 sort_library (LibraryTypePtr lib)
193 int i;
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);
200 static void
201 sort_netlist ()
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
211 FILE *
212 CheckAndOpenFile (char *Filename, Boolean Confirm, Boolean AllButton,
213 Boolean * WasAllButton, Boolean * WasCancelButton)
215 FILE *fp = NULL;
216 struct stat buffer;
217 char message[MAXPATHLEN + 80];
218 int response;
220 if (Filename && *Filename)
222 if (!stat (Filename, &buffer) && Confirm)
224 sprintf (message, _("File '%s' exists, use anyway?"), Filename);
225 if (WasAllButton)
226 *WasAllButton = False;
227 if (WasCancelButton)
228 *WasCancelButton = False;
229 if (AllButton)
230 response =
231 gui->confirm_dialog (message, "Cancel", "Ok",
232 AllButton ? "Sequence OK" : 0);
233 else
234 response =
235 gui->confirm_dialog (message, "Cancel", "Ok", "Sequence OK");
237 switch (response)
239 case 2:
240 if (WasAllButton)
241 *WasAllButton = True;
242 break;
243 case 0:
244 if (WasCancelButton)
245 *WasCancelButton = True;
248 if ((fp = fopen (Filename, "w")) == NULL)
249 OpenErrorMessage (Filename);
251 return (fp);
254 /* ---------------------------------------------------------------------------
255 * opens a file for saving connection data
257 FILE *
258 OpenConnectionDataFile (void)
260 char *fname;
261 FILE *fp;
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",
270 if (fname == NULL)
271 return NULL;
273 if (default_file != NULL)
275 free (default_file);
276 default_file = NULL;
279 if (fname && *fname)
280 default_file = strdup (fname);
282 fp = CheckAndOpenFile (fname, True, False, &result, NULL);
283 free (fname);
285 return fp;
288 /* ---------------------------------------------------------------------------
289 * save elements in the current buffer
292 SaveBufferElements (char *Filename)
294 int result;
296 if (SWAP_IDENT)
297 SwapBuffers ();
298 result = WritePipe (Filename, False);
299 if (SWAP_IDENT)
300 SwapBuffers ();
301 return (result);
304 /* ---------------------------------------------------------------------------
305 * save PCB
308 SavePCB (char *Filename)
310 int retcode;
311 char *copy;
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);
324 return (retcode);
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
330 * style.
332 static void
333 set_some_route_style ()
335 if (hid_get_flag ("style"))
336 return;
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 /* ---------------------------------------------------------------------------
344 * load PCB
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);
352 Boolean units_mm;
354 /* new data isn't added to the undo list */
355 if (!ParsePCB (newPCB, Filename))
357 RemovePCB (PCB);
358 PCB = newPCB;
360 CreateNewPCBPost (PCB, 0);
361 ResetStackAndVisibility ();
363 /* update cursor location */
364 Crosshair.X = MAX (0, MIN (PCB->CursorX, (LocationType) PCB->MaxWidth));
365 Crosshair.Y =
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)
377 Message (_
378 ("File '%s' has no font information, using default font\n"),
379 Filename);
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;
392 sort_netlist ();
394 set_some_route_style ();
396 hid_action ("PCBChanged");
398 return (0);
400 hid_action ("PCBChanged");
402 /* release unused memory */
403 RemovePCB (newPCB);
404 return (1);
407 /* ---------------------------------------------------------------------------
408 * functions for loading elements-as-pcb
411 extern PCBTypePtr yyPCB;
412 extern DataTypePtr yyData;
413 extern FontTypePtr yyFont;
415 void
416 PreLoadElementPCB ()
419 if (!yyPCB)
420 return;
422 yyFont = &yyPCB->Font;
423 yyData = yyPCB->Data;
424 yyData->pcb = (void *)yyPCB;
425 yyData->LayerN = 0;
428 void
429 PostLoadElementPCB ()
431 PCBTypePtr pcb_save = PCB;
432 ElementTypePtr e;
434 if (!yyPCB)
435 return;
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 */
440 PCB = yyPCB;
441 MoveElementLowLevel (yyPCB->Data,
442 e, -e->BoundingBox.X1, -e->BoundingBox.Y1);
443 PCB = pcb_save;
444 yyPCB->MaxWidth = e->BoundingBox.X2;
445 yyPCB->MaxHeight = e->BoundingBox.Y2;
448 /* ---------------------------------------------------------------------------
449 * writes the quoted string created by another subroutine
451 static void
452 PrintQuotedString (FILE * FP, char *S)
454 static DynamicStringType ds;
456 CreateQuotedString (&ds, S);
457 fputs (ds.Data, FP);
460 /* ---------------------------------------------------------------------------
461 * writes out an attribute list
463 static void
464 WriteAttributeList (FILE * FP, AttributeListTypePtr list, char *prefix)
466 int i;
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
477 static void
478 WritePCBInfoHeader (FILE * FP)
480 #ifdef HAVE_GETPWUID
481 struct passwd *pwentry;
482 #endif
484 #ifdef HAVE_GETHOSTNAME
485 static struct hostent *hostentry = NULL;
486 char hostname[256];
487 #endif
488 time_t currenttime;
490 /* write some useful comments */
491 currenttime = time (NULL);
492 fprintf (FP, "# release: %s " VERSION "\n", Progname);
493 fprintf (FP, "# date: %s", asctime (localtime (&currenttime)));
495 #ifdef HAVE_GETPWUID
496 pwentry = getpwuid (getuid ());
497 fprintf (FP, "# user: %s (%s)\n", pwentry->pw_name, pwentry->pw_gecos);
498 #else
499 fprintf (FP, "# user: Unknown\n");
500 #endif
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);
510 #else
511 fprintf (FP, "# host: Unknown\n");
512 #endif
515 /* ---------------------------------------------------------------------------
516 * writes data header
517 * the name of the PCB, cursor location, zoom and grid
518 * layergroups and some flags
520 static void
521 WritePCBDataHeader (FILE * FP)
523 Cardinal group;
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
568 static void
569 WritePCBFontData (FILE * FP)
571 Cardinal i, j;
572 LineTypePtr line;
573 FontTypePtr font;
575 for (font = &PCB->Font, i = 0; i <= MAX_FONTPOSITION; i++)
577 if (!font->Symbol[i].Valid)
578 continue;
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);
586 else
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);
600 else
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);
605 fputs (")\n", FP);
609 /* ---------------------------------------------------------------------------
610 * writes via data
612 static void
613 WriteViaData (FILE * FP, DataTypePtr Data)
615 int n;
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 ",
621 via->X, via->Y,
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
631 static void
632 WritePCBRatData (FILE * FP)
634 int n;
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
650 static void
651 WritePCBNetlistData (FILE * FP)
653 /* write out the netlist if it exists */
654 if (PCB->NetlistLib.MenuN)
656 int n, p;
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]);
664 fprintf (FP, " ");
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);
672 fprintf (FP, ")\n");
674 fprintf (FP, "\t)\n");
676 fprintf (FP, ")\n");
680 /* ---------------------------------------------------------------------------
681 * writes element data
683 static void
684 WriteElementData (FILE * FP, DataTypePtr Data)
686 int n, p;
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
692 && !element->PadN)
693 continue;
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)));
699 fputc (' ', FP);
700 PrintQuotedString (FP, EMPTY (NAMEONPCB_NAME (element)));
701 fputc (' ', FP);
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 -
706 element->MarkX),
707 (int) (DESCRIPTION_TEXT (element).Y -
708 element->MarkY),
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));
722 fprintf (FP, " ");
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,
735 (int) pad->Mask);
736 PrintQuotedString (FP, EMPTY (pad->Name));
737 fprintf (FP, " ");
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];
744 fprintf (FP,
745 "\tElementLine [%i %i %i %i %i]\n",
746 (int) (line->Point1.X -
747 element->MarkX),
748 (int) (line->Point1.Y -
749 element->MarkY),
750 (int) (line->Point2.X -
751 element->MarkX),
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];
758 fprintf (FP,
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 /* ---------------------------------------------------------------------------
771 * writes layer data
773 static void
774 WriteLayerData (FILE * FP, Cardinal Number, LayerTypePtr layer)
776 int n;
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];
816 int p, i = 0;
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];
821 if (i++ % 5 == 0)
822 fputs ("\n\t\t", FP);
823 fprintf (FP, "[%i %i] ", (int) point->X, (int) point->Y);
825 fputs ("\n\t)\n", FP);
827 fputs (")\n", FP);
831 /* ---------------------------------------------------------------------------
832 * writes just the elements in the buffer to file
834 static int
835 WriteBuffer (FILE * FP)
837 Cardinal i;
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]));
843 return (STATUS_OK);
846 /* ---------------------------------------------------------------------------
847 * writes PCB to file
849 static int
850 WritePCB (FILE * FP)
852 Cardinal i;
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);
865 return (STATUS_OK);
868 /* ---------------------------------------------------------------------------
869 * writes PCB to file
871 static int
872 WritePCBFile (char *Filename)
874 FILE *fp;
875 int result;
877 if ((fp = fopen (Filename, "w")) == NULL)
879 OpenErrorMessage (Filename);
880 return (STATUS_ERROR);
882 result = WritePCB (fp);
883 fclose (fp);
884 return (result);
887 /* ---------------------------------------------------------------------------
888 * writes to pipe using the command defined by Settings.SaveCommand
889 * %f are replaced by the passed filename
891 static int
892 WritePipe (char *Filename, Boolean thePcb)
894 FILE *fp;
895 int result;
896 char *p;
897 static DynamicStringType command;
898 int used_popen = 0;
900 if (EMPTY_STRING_P (Settings.SaveCommand))
902 fp = fopen (Filename, "w");
903 if (fp == 0)
905 Message ("Unable to write to file %s\n", Filename);
906 return STATUS_ERROR;
909 else
911 used_popen = 1;
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);
919 else
921 DSAddString (&command, Filename);
923 /* skip the character */
924 p++;
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);
935 if (thePcb)
936 result = WritePCB (fp);
937 else
938 result = WriteBuffer (fp);
940 if (used_popen)
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
950 void
951 SaveInTMP (void)
953 char filename[80];
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;
969 void
970 EmergencySave (void)
973 if (!dont_save_any_more)
975 SaveInTMP ();
976 dont_save_any_more = True;
980 void
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.
997 static void
998 backup_cb (hidval data)
1000 backup_timer.ptr = NULL;
1001 Backup ();
1002 if (Settings.BackupInterval > 0 && gui->add_timer)
1003 backup_timer = gui->add_timer (backup_cb,
1004 1000 * Settings.BackupInterval, data);
1007 void
1008 EnableAutosave (void)
1010 hidval x;
1012 x.ptr = NULL;
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
1031 void
1032 Backup (void)
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");
1042 exit (1);
1044 sprintf (filename, "%s-", PCB->Filename);
1046 else
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");
1053 exit (1);
1055 sprintf (filename, BACKUP_NAME, (int) getpid ());
1058 WritePCBFile (filename);
1059 free (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
1067 * the program.
1069 void
1070 SaveTMPData (void)
1072 sprintf (TMPFilename, EMERGENCY_NAME, (int) getpid ());
1073 WritePCBFile (TMPFilename);
1076 /* ---------------------------------------------------------------------------
1077 * removes the temporary copy of the data file
1079 void
1080 RemoveTMPData (void)
1082 unlink (TMPFilename);
1084 #endif
1086 /* ---------------------------------------------------------------------------
1087 * parse the directory tree where additional library elements are found
1090 static char *
1091 pcb_basename (char *p)
1093 char *rv = strrchr (p, '/');
1094 if (rv)
1095 return rv + 1;
1096 return p;
1100 ParseLibraryTree (void)
1102 char path[MAXPATHLEN + 1];
1103 char working[MAXPATHLEN + 1];
1104 char *libpaths, *p;
1105 DIR *dir, *subdir;
1106 struct stat buffer;
1107 struct dirent *direntry, *e2;
1108 LibraryMenuTypePtr menu = NULL;
1109 LibraryEntryTypePtr entry;
1110 size_t l;
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
1127 * relative path
1129 chdir (working);
1131 if ((dir = opendir (path)) == NULL)
1133 OpendirErrorMessage (path);
1134 continue;
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.
1142 chdir (path);
1143 GetWorkingDirectory (path);
1145 /* read all entries */
1146 while ((direntry = readdir (dir)) != NULL)
1148 chdir (path);
1149 /* find directories
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()");
1163 /* FIXME ? */
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
1186 * add 4
1188 long len = strlen (path) + strlen (e2->d_name) +
1189 strlen (direntry->d_name) + 4;
1190 #if 0
1191 if (NSTRCMP
1192 (e2->d_name + strlen (e2->d_name) - 4, ".lel") != 0)
1193 break;
1194 #endif
1195 n_footprints++;
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;
1210 closedir (subdir);
1213 closedir (dir);
1215 free (libpaths);
1217 /* restore the original working directory */
1218 chdir (working);
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;
1235 MYFREE (command);
1236 command = EvaluateFilename (Settings.LibraryContentsCommand,
1237 Settings.LibraryPath, Settings.LibraryFilename,
1238 NULL);
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 */
1254 if (len)
1256 len--;
1257 if (inputline[len] != '\n')
1258 Message
1259 ("linelength (%i) exceeded; following characters will be ignored\n",
1260 MAX_LIBRARY_LINE_LENGTH);
1261 else
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);
1273 else
1275 /* allocate a new menu entry if not already done */
1276 if (!menu)
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, ":")) !=
1289 NULL)
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)
1305 pclose (resultFP);
1307 if (ParseLibraryTree () > 0 || resultFP != NULL)
1309 sort_library (&Library);
1310 return 0;
1313 return (1);
1316 #define BLANK(x) ((x) == ' ' || (x) == '\t' || (x) == '\n' \
1317 || (x) == '\0')
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];
1329 FILE *fp;
1330 LibraryMenuTypePtr menu = NULL;
1331 LibraryEntryTypePtr entry;
1332 int i, j, lines, kind;
1333 Boolean continued;
1334 int used_popen = 0;
1336 if (!filename)
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");
1344 if (!fp)
1346 Message("Cannot open %s for reading", filename);
1347 return 1;
1350 else
1352 used_popen = 1;
1353 MYFREE (command);
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);
1361 return (1);
1364 lines = 0;
1365 /* kind = 0 is net name
1366 * kind = 1 is route style name
1367 * kind = 2 is connection
1369 kind = 0;
1370 while (fgets (inputline, MAX_NETLIST_LINE_LENGTH, fp))
1372 size_t len = strlen (inputline);
1373 /* check for maximum length line */
1374 if (len)
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);
1380 else
1381 inputline[len] = '\0';
1383 continued = (inputline[len - 1] == '\\') ? True : False;
1384 if (continued)
1385 inputline[len - 1] = '\0';
1386 lines++;
1387 i = 0;
1388 while (inputline[i] != '\0')
1390 j = 0;
1391 /* skip leading blanks */
1392 while (inputline[i] != '\0' && BLANK (inputline[i]))
1393 i++;
1394 if (kind == 0)
1396 /* add two spaces for included/unincluded */
1397 temp[j++] = ' ';
1398 temp[j++] = ' ';
1400 while (!BLANK (inputline[i]))
1401 temp[j++] = inputline[i++];
1402 temp[j] = '\0';
1403 while (inputline[i] != '\0' && BLANK (inputline[i]))
1404 i++;
1405 if (kind == 0)
1407 menu = GetLibraryMenuMemory (&PCB->NetlistLib);
1408 menu->Name = MyStrdup (temp, "ReadNetlist()");
1409 menu->flag = 1;
1410 kind++;
1412 else
1414 if (kind == 1 && strchr (temp, '-') == NULL)
1416 kind++;
1417 menu->Style = MyStrdup (temp, "ReadNetlist()");
1419 else
1421 entry = GetLibraryEntryMemory (menu);
1422 entry->ListEntry = MyStrdup (temp, "ReadNetlist()");
1426 if (!continued)
1427 kind = 0;
1429 if (!lines)
1431 Message (_("Empty netlist file!\n"));
1432 pclose (fp);
1433 return (1);
1435 if (used_popen)
1436 pclose (fp);
1437 else
1438 fclose (fp);
1439 sort_netlist ();
1440 return (0);
1443 static int ReadEdifNetlist (char *filename);
1445 int ImportNetlist (char *filename)
1447 FILE *fp;
1448 char buf[16];
1449 int i;
1450 char* p;
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);
1457 fclose(fp);
1458 buf[i] = '\0';
1459 p=buf;
1460 while ( *p )
1462 *p = tolower ((int) *p);
1463 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);
1475 return 0;