find.c: Remove unused APIs SaveFindFlag() and RestoreFindFlag()
[geda-pcb/pcjc2.git] / src / file.c
blob01d3b3921c54495d69510d926213665de2191887
1 /*
2 * COPYRIGHT
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
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
36 #ifdef HAVE_SYS_PARAM_H
37 #include <sys/param.h>
38 #endif
40 #include "global.h"
42 #include <dirent.h>
43 #ifdef HAVE_PWD_H
44 #include <pwd.h>
45 #endif
46 #include <time.h>
48 #ifdef HAVE_SYS_SOCKET_H
49 #include <sys/socket.h>
50 #endif
52 #include <sys/stat.h>
54 #ifdef HAVE_NETINET_IN_H
55 #include <netinet/in.h>
56 #endif
58 #ifdef HAVE_NETDB_H
59 #include <netdb.h>
60 #endif
62 #include <stdio.h>
64 #ifdef HAVE_STDLIB_H
65 #include <stdlib.h>
66 #endif
68 #ifdef HAVE_STRING_H
69 #include <string.h>
70 #endif
72 #ifdef HAVE_UNISTD_H
73 #include <unistd.h>
74 #endif
77 #include "buffer.h"
78 #include "change.h"
79 #include "create.h"
80 #include "crosshair.h"
81 #include "data.h"
82 #include "edif_parse.h"
83 #include "error.h"
84 #include "file.h"
85 #include "hid.h"
86 #include "misc.h"
87 #include "move.h"
88 #include "mymem.h"
89 #include "parse_l.h"
90 #include "pcb-printf.h"
91 #include "polygon.h"
92 #include "rats.h"
93 #include "remove.h"
94 #include "set.h"
95 #include "strflags.h"
97 #ifdef HAVE_LIBDMALLOC
98 #include <dmalloc.h>
99 #endif
101 #if !defined(HAS_ATEXIT) && !defined(HAS_ON_EXIT)
102 /* ---------------------------------------------------------------------------
103 * some local identifiers for OS without an atexit() or on_exit()
104 * call
106 static char TMPFilename[80];
107 #endif
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;
169 ENDALL_LOOP;
171 return PCB_FILE_VERSION_BASELINE;
174 /* --------------------------------------------------------------------------- */
176 static int
177 string_cmp (const char *a, const char *b)
179 while (*a && *b)
181 if (isdigit ((int) *a) && isdigit ((int) *b))
183 int ia = atoi (a);
184 int ib = atoi (b);
185 if (ia != ib)
186 return ia - ib;
187 while (isdigit ((int) *a) && *(a+1))
188 a++;
189 while (isdigit ((int) *b) && *(b+1))
190 b++;
192 else if (tolower ((int) *a) != tolower ((int) *b))
193 return tolower ((int) *a) - tolower ((int) *b);
194 a++;
195 b++;
197 if (*a)
198 return 1;
199 if (*b)
200 return -1;
201 return 0;
204 static int netlist_sort_offset = 0;
206 static int
207 netlist_sort (const void *va, const void *vb)
209 LibraryMenuType *am = (LibraryMenuType *) va;
210 LibraryMenuType *bm = (LibraryMenuType *) vb;
211 char *a = am->Name;
212 char *b = bm->Name;
213 if (*a == '~')
214 a++;
215 if (*b == '~')
216 b++;
217 return string_cmp (a, b);
220 static int
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);
230 static void
231 sort_library (LibraryType *lib)
233 int i;
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);
240 void
241 sort_netlist ()
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
251 FILE *
252 CheckAndOpenFile (char *Filename, bool Confirm, bool AllButton,
253 bool * WasAllButton, bool * WasCancelButton)
255 FILE *fp = NULL;
256 struct stat buffer;
257 char message[MAXPATHLEN + 80];
258 int response;
260 if (Filename && *Filename)
262 if (!stat (Filename, &buffer) && Confirm)
264 sprintf (message, _("File '%s' exists, use anyway?"), Filename);
265 if (WasAllButton)
266 *WasAllButton = false;
267 if (WasCancelButton)
268 *WasCancelButton = false;
269 if (AllButton)
270 response =
271 gui->confirm_dialog (message, "Cancel", "Ok",
272 AllButton ? "Sequence OK" : 0);
273 else
274 response =
275 gui->confirm_dialog (message, "Cancel", "Ok", "Sequence OK");
277 switch (response)
279 case 2:
280 if (WasAllButton)
281 *WasAllButton = true;
282 break;
283 case 0:
284 if (WasCancelButton)
285 *WasCancelButton = true;
288 if ((fp = fopen (Filename, "w")) == NULL)
289 OpenErrorMessage (Filename);
291 return (fp);
294 /* ---------------------------------------------------------------------------
295 * opens a file for saving connection data
297 FILE *
298 OpenConnectionDataFile (void)
300 char *fname;
301 FILE *fp;
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",
310 if (fname == NULL)
311 return NULL;
313 if (default_file != NULL)
315 free (default_file);
316 default_file = NULL;
319 if (fname && *fname)
320 default_file = strdup (fname);
322 fp = CheckAndOpenFile (fname, true, false, &result, NULL);
323 free (fname);
325 return fp;
328 /* ---------------------------------------------------------------------------
329 * save elements in the current buffer
332 SaveBufferElements (char *Filename)
334 int result;
336 if (SWAP_IDENT)
337 SwapBuffers ();
338 result = WritePipe (Filename, false);
339 if (SWAP_IDENT)
340 SwapBuffers ();
341 return (result);
344 /* ---------------------------------------------------------------------------
345 * save PCB
348 SavePCB (char *file)
350 int retcode;
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);
359 return retcode;
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
365 * style.
367 static void
368 set_some_route_style ()
370 if (hid_get_flag ("style"))
371 return;
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 /* ---------------------------------------------------------------------------
379 * load PCB
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.
386 static int
387 real_load_pcb (char *Filename, bool revert)
389 const char *unit_suffix, *grid_size;
390 char *new_filename;
391 PCBType *newPCB = CreateNewPCB (false);
392 PCBType *oldPCB;
393 #ifdef DEBUG
394 double elapsed;
395 clock_t start, end;
397 start = clock ();
398 #endif
400 new_filename = strdup (Filename);
402 oldPCB = PCB;
403 PCB = newPCB;
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))
411 RemovePCB (oldPCB);
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)
426 Message (_
427 ("File '%s' has no font information, using default font\n"),
428 new_filename);
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);
442 if (new_unit)
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");
448 if (grid_size)
450 PCB->Grid = GetValue (grid_size, NULL, NULL);
453 sort_netlist ();
455 set_some_route_style ();
457 if (revert)
458 hid_actionl ("PCBChanged", "revert", NULL);
459 else
460 hid_action ("PCBChanged");
462 #ifdef DEBUG
463 end = clock ();
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);
467 #endif
469 return (0);
471 PCB = oldPCB;
472 hid_action ("PCBChanged");
474 /* release unused memory */
475 RemovePCB (newPCB);
476 return (1);
479 /* ---------------------------------------------------------------------------
480 * Load PCB
483 LoadPCB (char *file)
485 return real_load_pcb (file, false);
488 /* ---------------------------------------------------------------------------
489 * Revert PCB
492 RevertPCB (void)
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;
505 void
506 PreLoadElementPCB ()
509 if (!yyPCB)
510 return;
512 yyFont = &yyPCB->Font;
513 yyData = yyPCB->Data;
514 yyData->pcb = yyPCB;
515 yyData->LayerN = 0;
518 void
519 PostLoadElementPCB ()
521 PCBType *pcb_save = PCB;
522 ElementType *e;
524 if (!yyPCB)
525 return;
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 */
530 PCB = yyPCB;
531 MoveElementLowLevel (yyPCB->Data,
532 e, -e->BoundingBox.X1, -e->BoundingBox.Y1);
533 PCB = pcb_save;
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
542 static void
543 PrintQuotedString (FILE * FP, char *S)
545 static DynamicStringType ds;
547 CreateQuotedString (&ds, S);
548 fputs (ds.Data, FP);
551 /* ---------------------------------------------------------------------------
552 * writes out an attribute list
554 static void
555 WriteAttributeList (FILE * FP, AttributeListType *list, char *prefix)
557 int i;
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
567 static void
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 /* ---------------------------------------------------------------------------
579 * writes data header
580 * the name of the PCB, cursor location, zoom and grid
581 * layergroups and some flags
583 static void
584 WritePCBDataHeader (FILE * FP)
586 Cardinal group;
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
629 static void
630 WritePCBFontData (FILE * FP)
632 Cardinal i, j;
633 LineType *line;
634 FontType *font;
636 for (font = &PCB->Font, i = 0; i <= MAX_FONTPOSITION; i++)
638 if (!font->Symbol[i].Valid)
639 continue;
641 if (isprint (i))
642 pcb_fprintf (FP, "Symbol['%c' %mr]\n(\n", i, font->Symbol[i].Delta);
643 else
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);
651 fputs (")\n", FP);
655 /* ---------------------------------------------------------------------------
656 * writes via data
658 static void
659 WriteViaData (FILE * FP, DataType *Data)
661 GList *iter;
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
676 static void
677 WritePCBRatData (FILE * FP)
679 GList *iter;
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
694 static void
695 WritePCBNetlistData (FILE * FP)
697 /* write out the netlist if it exists */
698 if (PCB->NetlistLib.MenuN)
700 int n, p;
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]);
708 fprintf (FP, " ");
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);
716 fprintf (FP, ")\n");
718 fprintf (FP, "\t)\n");
720 fprintf (FP, ")\n");
724 /* ---------------------------------------------------------------------------
725 * writes element data
727 static void
728 WriteElementData (FILE * FP, DataType *Data)
730 GList *n, *p;
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
737 && !element->PadN)
738 continue;
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)));
744 fputc (' ', FP);
745 PrintQuotedString (FP, (char *)EMPTY (NAMEONPCB_NAME (element)));
746 fputc (' ', FP);
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));
765 fprintf (FP, " ");
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));
779 fprintf (FP, " ");
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,
791 line->Thickness);
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,
801 arc->Thickness);
803 fputs ("\n\t)\n", FP);
807 /* ---------------------------------------------------------------------------
808 * writes layer data
810 static void
811 WriteLayerData (FILE * FP, Cardinal Number, LayerType *layer)
813 GList *n;
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 ",
845 text->X, text->Y,
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;
853 int p, i = 0;
854 Cardinal hole = 0;
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])
863 if (hole > 0)
864 fputs ("\n\t\t)", FP);
865 fputs ("\n\t\tHole (", FP);
866 hole++;
867 i = 0;
870 if (i++ % 5 == 0)
872 fputs ("\n\t\t", FP);
873 if (hole)
874 fputs ("\t", FP);
876 pcb_fprintf (FP, "[%mr %mr] ", point->X, point->Y);
878 if (hole > 0)
879 fputs ("\n\t\t)", FP);
880 fputs ("\n\t)\n", FP);
882 fputs (")\n", FP);
886 /* ---------------------------------------------------------------------------
887 * writes just the elements in the buffer to file
889 static int
890 WriteBuffer (FILE * FP)
892 Cardinal i;
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]));
898 return (STATUS_OK);
901 /* ---------------------------------------------------------------------------
902 * writes PCB to file
904 static int
905 WritePCB (FILE * FP)
907 Cardinal i;
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);
920 return (STATUS_OK);
923 /* ---------------------------------------------------------------------------
924 * writes PCB to file
926 static int
927 WritePCBFile (char *Filename)
929 FILE *fp;
930 int result;
932 if ((fp = fopen (Filename, "w")) == NULL)
934 OpenErrorMessage (Filename);
935 return (STATUS_ERROR);
937 result = WritePCB (fp);
938 fclose (fp);
939 return (result);
942 /* ---------------------------------------------------------------------------
943 * writes to pipe using the command defined by Settings.SaveCommand
944 * %f are replaced by the passed filename
946 static int
947 WritePipe (char *Filename, bool thePcb)
949 FILE *fp;
950 int result;
951 char *p;
952 static DynamicStringType command;
953 int used_popen = 0;
955 if (EMPTY_STRING_P (Settings.SaveCommand))
957 fp = fopen (Filename, "w");
958 if (fp == 0)
960 Message ("Unable to write to file %s\n", Filename);
961 return STATUS_ERROR;
964 else
966 used_popen = 1;
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);
974 else
976 DSAddString (&command, Filename);
978 /* skip the character */
979 p++;
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);
990 if (thePcb)
992 if (PCB->is_footprint)
994 WriteElementData (fp, PCB->Data);
995 result = 0;
997 else
998 result = WritePCB (fp);
1000 else
1001 result = WriteBuffer (fp);
1003 if (used_popen)
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
1013 void
1014 SaveInTMP (void)
1016 char filename[80];
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;
1032 void
1033 EmergencySave (void)
1036 if (!dont_save_any_more)
1038 SaveInTMP ();
1039 dont_save_any_more = true;
1043 void
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.
1060 static void
1061 backup_cb (hidval data)
1063 backup_timer.ptr = NULL;
1064 Backup ();
1065 if (Settings.BackupInterval > 0 && gui->add_timer)
1066 backup_timer = gui->add_timer (backup_cb,
1067 1000 * Settings.BackupInterval, data);
1070 void
1071 EnableAutosave (void)
1073 hidval x;
1075 x.ptr = NULL;
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
1094 void
1095 Backup (void)
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");
1105 exit (1);
1107 sprintf (filename, "%s-", PCB->Filename);
1109 else
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");
1116 exit (1);
1118 sprintf (filename, BACKUP_NAME, (int) getpid ());
1121 WritePCBFile (filename);
1122 free (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
1130 * the program.
1132 void
1133 SaveTMPData (void)
1135 sprintf (TMPFilename, EMERGENCY_NAME, (int) getpid ());
1136 WritePCBFile (TMPFilename);
1139 /* ---------------------------------------------------------------------------
1140 * removes the temporary copy of the data file
1142 void
1143 RemoveTMPData (void)
1145 unlink (TMPFilename);
1147 #endif
1149 /* ---------------------------------------------------------------------------
1150 * Parse the directory tree where newlib footprints are found
1153 /* Helper function for ParseLibraryTree */
1154 static char *
1155 pcb_basename (char *p)
1157 char *rv = strrchr (p, '/');
1158 if (rv)
1159 return rv + 1;
1160 return 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.
1167 static int
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 */
1177 size_t l;
1178 size_t len;
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"));
1187 return 0;
1190 if (strcmp (libpath, "(local)") == 0)
1191 strcpy (subdir, ".");
1192 else
1193 strcpy (subdir, libpath);
1195 if (chdir (subdir))
1197 ChdirErrorMessage (subdir);
1198 return 0;
1201 /* Determine subdir is abs path */
1202 if (GetWorkingDirectory (subdir) == NULL)
1204 Message (_("LoadNewlibFootprintsFromDir: Could not determine new working directory\n"));
1205 if (chdir (olddir))
1206 ChdirErrorMessage (olddir);
1207 return 0;
1210 /* First try opening the directory specified by path */
1211 if ( (subdirobj = opendir (subdir)) == NULL )
1213 OpendirErrorMessage (subdir);
1214 if (chdir (olddir))
1215 ChdirErrorMessage (olddir);
1216 return 0;
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)
1230 #ifdef DEBUG
1231 /* printf("... Examining file %s ... \n", subdirentry->d_name); */
1232 #endif
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) )
1250 #ifdef DEBUG
1251 /* printf("... Found a footprint %s ... \n", subdirentry->d_name); */
1252 #endif
1253 n_footprints++;
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);
1278 if (chdir (olddir))
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
1289 * datastructures.
1291 static int
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"));
1313 return 0;
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
1326 * relative path
1328 if (chdir (working))
1330 ChdirErrorMessage (working);
1331 free (libpaths);
1332 return 0;
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);
1342 continue;
1345 if (GetWorkingDirectory (toppath) == NULL)
1347 Message (_("ParseLibraryTree: Could not determine new working directory\n"));
1348 continue;
1351 #ifdef DEBUG
1352 printf("In ParseLibraryTree, looking for newlib footprints inside top level directory %s ... \n",
1353 toppath);
1354 #endif
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);
1363 continue;
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)
1372 #ifdef DEBUG
1373 printf("In ParseLibraryTree loop examining 2nd level direntry %s ... \n", direntry->d_name);
1374 #endif
1375 /* Find subdirectories. Ignore entries beginning with "." and CVS
1376 * directories.
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);
1388 closedir (dirobj);
1391 /* restore the original working directory */
1392 if (chdir (working))
1393 ChdirErrorMessage (working);
1395 #ifdef DEBUG
1396 printf("Leaving ParseLibraryTree, found %d footprints.\n", n_footprints);
1397 #endif
1399 free (libpaths);
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
1406 * footprints.
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
1425 * points to it.
1427 free (command);
1428 command = EvaluateFilename (Settings.LibraryContentsCommand,
1429 Settings.LibraryPath, Settings.LibraryFilename,
1430 NULL);
1432 #ifdef DEBUG
1433 printf("In ReadLibraryContents, about to execute command %s\n", command);
1434 #endif
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 */
1452 if (len)
1454 len--;
1455 if (inputline[len] != '\n')
1456 Message
1457 ("linelength (%i) exceeded; following characters will be ignored\n",
1458 MAX_LIBRARY_LINE_LENGTH);
1459 else
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);
1470 else
1472 /* allocate a new menu entry if not already done */
1473 if (!menu)
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, ":")) !=
1484 NULL)
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)
1499 pclose (resultFP);
1502 /* Now after reading in the M4 libs, call a function to
1503 * read the newlib footprint libraries. Then sort the whole
1504 * library.
1506 if (ParseLibraryTree () > 0 || resultFP != NULL)
1508 sort_library (&Library);
1509 return 0;
1512 return (1);
1515 #define BLANK(x) ((x) == ' ' || (x) == '\t' || (x) == '\n' \
1516 || (x) == '\0')
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];
1528 FILE *fp;
1529 LibraryMenuType *menu = NULL;
1530 LibraryEntryType *entry;
1531 int i, j, lines, kind;
1532 bool continued;
1533 bool used_popen = false;
1534 int retval = 0;
1536 if (!filename)
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");
1544 if (!fp)
1546 Message("Cannot open %s for reading", filename);
1547 return 1;
1550 else
1552 used_popen = true;
1553 free (command);
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);
1561 return 1;
1564 lines = 0;
1565 /* kind = 0 is net name
1566 * kind = 1 is route style name
1567 * kind = 2 is connection
1569 kind = 0;
1570 while (fgets (inputline, MAX_NETLIST_LINE_LENGTH, fp))
1572 size_t len = strlen (inputline);
1573 /* check for maximum length line */
1574 if (len)
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);
1580 else
1581 inputline[len] = '\0';
1583 continued = (inputline[len - 1] == '\\') ? true : false;
1584 if (continued)
1585 inputline[len - 1] = '\0';
1586 lines++;
1587 i = 0;
1588 while (inputline[i] != '\0')
1590 j = 0;
1591 /* skip leading blanks */
1592 while (inputline[i] != '\0' && BLANK (inputline[i]))
1593 i++;
1594 if (kind == 0)
1596 /* add two spaces for included/unincluded */
1597 temp[j++] = ' ';
1598 temp[j++] = ' ';
1600 while (!BLANK (inputline[i]))
1601 temp[j++] = inputline[i++];
1602 temp[j] = '\0';
1603 while (inputline[i] != '\0' && BLANK (inputline[i]))
1604 i++;
1605 if (kind == 0)
1607 menu = GetLibraryMenuMemory (&PCB->NetlistLib);
1608 menu->Name = strdup (temp);
1609 menu->flag = 1;
1610 kind++;
1612 else
1614 if (kind == 1 && strchr (temp, '-') == NULL)
1616 kind++;
1617 menu->Style = strdup (temp);
1619 else
1621 entry = GetLibraryEntryMemory (menu);
1622 entry->ListEntry = strdup (temp);
1626 if (!continued)
1627 kind = 0;
1629 if (!lines)
1631 Message (_("Empty netlist file!\n"));
1632 retval = 1;
1634 if (used_popen)
1635 pclose (fp);
1636 else
1637 fclose (fp);
1638 sort_netlist ();
1639 return retval;
1642 static int ReadEdifNetlist (char *filename);
1644 int ImportNetlist (char *filename)
1646 FILE *fp;
1647 char buf[16];
1648 int i;
1649 char* p;
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);
1656 fclose(fp);
1657 buf[i] = '\0';
1658 p=buf;
1659 while ( *p )
1661 *p = tolower ((int) *p);
1662 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);
1674 return 0;