(no commit message)
[geda-pcb/pcjc2.git] / src / file.c
blobb7759615185e162b5a4e0025f38a0df74b65e2ec
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 "mymem.h"
88 #include "parse_l.h"
89 #include "pcb-printf.h"
90 #include "polygon.h"
91 #include "rats.h"
92 #include "remove.h"
93 #include "set.h"
94 #include "strflags.h"
96 #ifdef HAVE_LIBDMALLOC
97 #include <dmalloc.h>
98 #endif
100 #if !defined(HAS_ATEXIT) && !defined(HAS_ON_EXIT)
101 /* ---------------------------------------------------------------------------
102 * some local identifiers for OS without an atexit() or on_exit()
103 * call
105 static char TMPFilename[80];
106 #endif
108 /* ---------------------------------------------------------------------------
109 * some local prototypes
111 static void PrintQuotedString (FILE *, char *);
112 static void WritePCBInfoHeader (FILE *);
113 static void WritePCBDataHeader (FILE *);
114 static void WritePCBFontData (FILE *);
115 static void WriteViaData (FILE *, DataType *);
116 static void WritePCBRatData (FILE *);
117 static void WriteElementData (FILE *, DataType *);
118 static void WriteLayerData (FILE *, Cardinal, LayerType *);
119 static int WritePCB (FILE *);
120 static int WritePCBFile (char *);
121 static int WritePipe (char *, bool);
122 static int ParseLibraryTree (void);
123 static int LoadNewlibFootprintsFromDir(char *path, char *toppath);
124 static char *pcb_basename (char *p);
126 /* ---------------------------------------------------------------------------
127 * Flag helper functions
130 #define F2S(OBJ, TYPE) flags_to_string ((OBJ)->Flags, TYPE)
132 /* --------------------------------------------------------------------------- */
134 /* The idea here is to avoid gratuitously breaking backwards
135 compatibility due to a new but rarely used feature. The first such
136 case, for example, was the polygon Hole - if your design included
137 polygon holes, you needed a newer PCB to read it, but if your
138 design didn't include holes, PCB would produce a file that older
139 PCBs could read, if only it had the correct version number in it.
141 If, however, you have to add or change a feature that really does
142 require a new PCB version all the time, it's time to remove all the
143 tests below and just always output the new version.
145 Note: Best practices here is to add support for a feature *first*
146 (and bump PCB_FILE_VERSION in file.h), and note the version that
147 added that support below, and *later* update the file format to
148 need that version (which may then be older than PCB_FILE_VERSION).
149 Hopefully, that allows for one release between adding support and
150 needing it, which should minimize breakage. Of course, that's not
151 *always* possible, practical, or desirable.
155 /* Hole[] in Polygon. */
156 #define PCB_FILE_VERSION_HOLES 20100606
157 /* First version ever saved. */
158 #define PCB_FILE_VERSION_BASELINE 20091103
161 PCBFileVersionNeeded (void)
163 ALLPOLYGON_LOOP (PCB->Data);
165 if (polygon->HoleIndexN > 0)
166 return PCB_FILE_VERSION_HOLES;
168 ENDALL_LOOP;
170 return PCB_FILE_VERSION_BASELINE;
173 /* --------------------------------------------------------------------------- */
175 static int
176 string_cmp (const char *a, const char *b)
178 while (*a && *b)
180 if (isdigit ((int) *a) && isdigit ((int) *b))
182 int ia = atoi (a);
183 int ib = atoi (b);
184 if (ia != ib)
185 return ia - ib;
186 while (isdigit ((int) *a) && *(a+1))
187 a++;
188 while (isdigit ((int) *b) && *(b+1))
189 b++;
191 else if (tolower ((int) *a) != tolower ((int) *b))
192 return tolower ((int) *a) - tolower ((int) *b);
193 a++;
194 b++;
196 if (*a)
197 return 1;
198 if (*b)
199 return -1;
200 return 0;
203 static int netlist_sort_offset = 0;
205 static int
206 netlist_sort (const void *va, const void *vb)
208 LibraryMenuType *am = (LibraryMenuType *) va;
209 LibraryMenuType *bm = (LibraryMenuType *) vb;
210 char *a = am->Name;
211 char *b = bm->Name;
212 if (*a == '~')
213 a++;
214 if (*b == '~')
215 b++;
216 return string_cmp (a, b);
219 static int
220 netnode_sort (const void *va, const void *vb)
222 LibraryEntryType *am = (LibraryEntryType *) va;
223 LibraryEntryType *bm = (LibraryEntryType *) vb;
224 char *a = am->ListEntry;
225 char *b = bm->ListEntry;
226 return string_cmp (a, b);
229 static void
230 sort_library (LibraryType *lib)
232 int i;
233 qsort (lib->Menu, lib->MenuN, sizeof (lib->Menu[0]), netlist_sort);
234 for (i = 0; i < lib->MenuN; i++)
235 qsort (lib->Menu[i].Entry,
236 lib->Menu[i].EntryN, sizeof (lib->Menu[i].Entry[0]), netnode_sort);
239 void
240 sort_netlist ()
242 netlist_sort_offset = 2;
243 sort_library (&(PCB->NetlistLib));
244 netlist_sort_offset = 0;
247 /* ---------------------------------------------------------------------------
248 * opens a file and check if it exists
250 FILE *
251 CheckAndOpenFile (char *Filename, bool Confirm, bool AllButton,
252 bool * WasAllButton, bool * WasCancelButton)
254 FILE *fp = NULL;
255 struct stat buffer;
256 char message[MAXPATHLEN + 80];
257 int response;
259 if (Filename && *Filename)
261 if (!stat (Filename, &buffer) && Confirm)
263 sprintf (message, _("File '%s' exists, use anyway?"), Filename);
264 if (WasAllButton)
265 *WasAllButton = false;
266 if (WasCancelButton)
267 *WasCancelButton = false;
268 if (AllButton)
269 response =
270 gui->confirm_dialog (message, "Cancel", "Ok",
271 AllButton ? "Sequence OK" : 0);
272 else
273 response =
274 gui->confirm_dialog (message, "Cancel", "Ok", "Sequence OK");
276 switch (response)
278 case 2:
279 if (WasAllButton)
280 *WasAllButton = true;
281 break;
282 case 0:
283 if (WasCancelButton)
284 *WasCancelButton = true;
287 if ((fp = fopen (Filename, "w")) == NULL)
288 OpenErrorMessage (Filename);
290 return (fp);
293 /* ---------------------------------------------------------------------------
294 * opens a file for saving connection data
296 FILE *
297 OpenConnectionDataFile (void)
299 char *fname;
300 FILE *fp;
301 static char * default_file = NULL;
302 bool result; /* not used */
304 /* CheckAndOpenFile deals with the case where fname already exists */
305 fname = gui->fileselect (_("Save Connection Data As ..."),
306 _("Choose a file to save all connection data to."),
307 default_file, ".net", "connection_data",
309 if (fname == NULL)
310 return NULL;
312 if (default_file != NULL)
314 free (default_file);
315 default_file = NULL;
318 if (fname && *fname)
319 default_file = strdup (fname);
321 fp = CheckAndOpenFile (fname, true, false, &result, NULL);
322 free (fname);
324 return fp;
327 /* ---------------------------------------------------------------------------
328 * save elements in the current buffer
331 SaveBufferElements (char *Filename)
333 int result;
335 if (SWAP_IDENT)
336 SwapBuffers ();
337 result = WritePipe (Filename, false);
338 if (SWAP_IDENT)
339 SwapBuffers ();
340 return (result);
343 /* ---------------------------------------------------------------------------
344 * save PCB
347 SavePCB (char *file)
349 int retcode;
351 if (gui->notify_save_pcb == NULL)
352 return WritePipe (file, true);
354 gui->notify_save_pcb (file, false);
355 retcode = WritePipe (file, true);
356 gui->notify_save_pcb (file, true);
358 return retcode;
361 /* ---------------------------------------------------------------------------
362 * set the route style to the first one, if the current one doesn't
363 * happen to match any. This way, "revert" won't change the route
364 * style.
366 static void
367 set_some_route_style ()
369 if (hid_get_flag ("style"))
370 return;
371 SetLineSize (PCB->RouteStyle[0].Thick);
372 SetViaSize (PCB->RouteStyle[0].Diameter, true);
373 SetViaDrillingHole (PCB->RouteStyle[0].Hole, true);
374 SetKeepawayWidth (PCB->RouteStyle[0].Keepaway);
377 /* ---------------------------------------------------------------------------
378 * load PCB
379 * parse the file with enabled 'PCB mode' (see parser)
380 * if successful, update some other stuff
382 * If revert is true, we pass "revert" as a parameter
383 * to the HID's PCBChanged action.
385 static int
386 real_load_pcb (char *Filename, bool revert)
388 const char *unit_suffix, *grid_size;
389 char *new_filename;
390 PCBType *newPCB = CreateNewPCB (false);
391 PCBType *oldPCB;
392 #ifdef DEBUG
393 double elapsed;
394 clock_t start, end;
396 start = clock ();
397 #endif
399 new_filename = strdup (Filename);
401 oldPCB = PCB;
402 PCB = newPCB;
404 /* mark the default font invalid to know if the file has one */
405 newPCB->Font.Valid = false;
407 /* new data isn't added to the undo list */
408 if (!ParsePCB (PCB, new_filename))
410 RemovePCB (oldPCB);
412 CreateNewPCBPost (PCB, 0);
413 ResetStackAndVisibility ();
415 /* update cursor location */
416 Crosshair.X = CLAMP (PCB->CursorX, 0, PCB->MaxWidth);
417 Crosshair.Y = CLAMP (PCB->CursorY, 0, PCB->MaxHeight);
419 /* update cursor confinement and output area (scrollbars) */
420 ChangePCBSize (PCB->MaxWidth, PCB->MaxHeight);
422 /* enable default font if necessary */
423 if (!PCB->Font.Valid)
425 Message (_
426 ("File '%s' has no font information, using default font\n"),
427 new_filename);
428 PCB->Font.Valid = true;
431 /* clear 'changed flag' */
432 SetChangedFlag (false);
433 PCB->Filename = new_filename;
434 /* just in case a bad file saved file is loaded */
436 /* Use attribute PCB::grid::unit as unit, if we can */
437 unit_suffix = AttributeGet (PCB, "PCB::grid::unit");
438 if (unit_suffix && *unit_suffix)
440 const Unit *new_unit = get_unit_struct (unit_suffix);
441 if (new_unit)
442 Settings.grid_unit = new_unit;
444 AttributePut (PCB, "PCB::grid::unit", Settings.grid_unit->suffix);
445 /* Use attribute PCB::grid::size as size, if we can */
446 grid_size = AttributeGet (PCB, "PCB::grid::size");
447 if (grid_size)
449 PCB->Grid = GetValue (grid_size, NULL, NULL);
452 sort_netlist ();
454 set_some_route_style ();
456 if (revert)
457 hid_actionl ("PCBChanged", "revert", NULL);
458 else
459 hid_action ("PCBChanged");
461 #ifdef DEBUG
462 end = clock ();
463 elapsed = ((double) (end - start)) / CLOCKS_PER_SEC;
464 gui->log ("Loading file %s took %f seconds of CPU time\n",
465 new_filename, elapsed);
466 #endif
468 return (0);
470 PCB = oldPCB;
471 hid_action ("PCBChanged");
473 /* release unused memory */
474 RemovePCB (newPCB);
475 return (1);
478 /* ---------------------------------------------------------------------------
479 * Load PCB
482 LoadPCB (char *file)
484 return real_load_pcb (file, false);
487 /* ---------------------------------------------------------------------------
488 * Revert PCB
491 RevertPCB (void)
493 return real_load_pcb (PCB->Filename, true);
496 /* ---------------------------------------------------------------------------
497 * writes the quoted string created by another subroutine
499 static void
500 PrintQuotedString (FILE * FP, char *S)
502 static DynamicStringType ds;
504 CreateQuotedString (&ds, S);
505 fputs (ds.Data, FP);
508 /* ---------------------------------------------------------------------------
509 * writes out an attribute list
511 static void
512 WriteAttributeList (FILE * FP, AttributeListType *list, char *prefix)
514 int i;
516 for (i = 0; i < list->Number; i++)
517 fprintf (FP, "%sAttribute(\"%s\" \"%s\")\n",
518 prefix, list->List[i].name, list->List[i].value);
521 /* ---------------------------------------------------------------------------
522 * writes layout header information
524 static void
525 WritePCBInfoHeader (FILE * FP)
527 /* write some useful comments */
528 fprintf (FP, "# release: %s " VERSION "\n", Progname);
530 /* avoid writing things like user name or date, as these cause merge
531 * conflicts in collaborative environments using version control systems
535 /* ---------------------------------------------------------------------------
536 * writes data header
537 * the name of the PCB, cursor location, zoom and grid
538 * layergroups and some flags
540 static void
541 WritePCBDataHeader (FILE * FP)
543 Cardinal group;
546 * ************************** README *******************
547 * ************************** README *******************
549 * If the file format is modified in any way, update
550 * PCB_FILE_VERSION in file.h as well as PCBFileVersionNeeded()
551 * at the top of this file.
553 * ************************** README *******************
554 * ************************** README *******************
557 fprintf (FP, "\n# To read pcb files, the pcb version (or the git source date) must be >= the file version\n");
558 fprintf (FP, "FileVersion[%i]\n", PCBFileVersionNeeded ());
560 fputs ("\nPCB[", FP);
561 PrintQuotedString (FP, (char *)EMPTY (PCB->Name));
562 pcb_fprintf (FP, " %mr %mr]\n\n", PCB->MaxWidth, PCB->MaxHeight);
563 pcb_fprintf (FP, "Grid[%s %mr %mr %d]\n", c_dtostr (COORD_TO_MIL (PCB->Grid) * 100), PCB->GridOffsetX, PCB->GridOffsetY, Settings.DrawGrid);
564 /* PolyArea should be output in square cmils, no suffix */
565 fprintf (FP, "PolyArea[%s]\n", c_dtostr (COORD_TO_MIL (COORD_TO_MIL (PCB->IsleArea) * 100) * 100));
566 pcb_fprintf (FP, "Thermal[%s]\n", c_dtostr (PCB->ThermScale));
567 pcb_fprintf (FP, "DRC[%mr %mr %mr %mr %mr %mr]\n", PCB->Bloat, PCB->Shrink,
568 PCB->minWid, PCB->minSlk, PCB->minDrill, PCB->minRing);
569 fprintf (FP, "Flags(%s)\n", pcbflags_to_string(PCB->Flags));
570 fprintf (FP, "Groups(\"%s\")\n", LayerGroupsToString (&PCB->LayerGroups));
571 fputs ("Styles[\"", FP);
572 for (group = 0; group < NUM_STYLES - 1; group++)
573 pcb_fprintf (FP, "%s,%mr,%mr,%mr,%mr:", PCB->RouteStyle[group].Name,
574 PCB->RouteStyle[group].Thick,
575 PCB->RouteStyle[group].Diameter,
576 PCB->RouteStyle[group].Hole, PCB->RouteStyle[group].Keepaway);
577 pcb_fprintf (FP, "%s,%mr,%mr,%mr,%mr\"]\n\n", PCB->RouteStyle[group].Name,
578 PCB->RouteStyle[group].Thick,
579 PCB->RouteStyle[group].Diameter,
580 PCB->RouteStyle[group].Hole, PCB->RouteStyle[group].Keepaway);
583 /* ---------------------------------------------------------------------------
584 * writes font data of non empty symbols
586 static void
587 WritePCBFontData (FILE * FP)
589 Cardinal i, j;
590 LineType *line;
591 FontType *font;
593 for (font = &PCB->Font, i = 0; i <= MAX_FONTPOSITION; i++)
595 if (!font->Symbol[i].Valid)
596 continue;
598 if (isprint (i))
599 pcb_fprintf (FP, "Symbol['%c' %mr]\n(\n", i, font->Symbol[i].Delta);
600 else
601 pcb_fprintf (FP, "Symbol[%i %mr]\n(\n", i, font->Symbol[i].Delta);
603 line = font->Symbol[i].Line;
604 for (j = font->Symbol[i].LineN; j; j--, line++)
605 pcb_fprintf (FP, "\tSymbolLine[%mr %mr %mr %mr %mr]\n",
606 line->Point1.X, line->Point1.Y,
607 line->Point2.X, line->Point2.Y, line->Thickness);
608 fputs (")\n", FP);
612 /* ---------------------------------------------------------------------------
613 * writes via data
615 static void
616 WriteViaData (FILE * FP, DataType *Data)
618 GList *iter;
619 /* write information about vias */
620 for (iter = Data->Via; iter != NULL; iter = g_list_next (iter))
622 PinType *via = iter->data;
623 pcb_fprintf (FP, "Via[%mr %mr %mr %mr %mr %mr ", via->X, via->Y,
624 via->Thickness, via->Clearance, via->Mask, via->DrillingHole);
625 PrintQuotedString (FP, (char *)EMPTY (via->Name));
626 fprintf (FP, " %s]\n", F2S (via, VIA_TYPE));
630 /* ---------------------------------------------------------------------------
631 * writes rat-line data
633 static void
634 WritePCBRatData (FILE * FP)
636 GList *iter;
637 /* write information about rats */
638 for (iter = PCB->Data->Rat; iter != NULL; iter = g_list_next (iter))
640 RatType *line = iter->data;
641 pcb_fprintf (FP, "Rat[%mr %mr %d %mr %mr %d ",
642 line->Point1.X, line->Point1.Y, line->group1,
643 line->Point2.X, line->Point2.Y, line->group2);
644 fprintf (FP, " %s]\n", F2S (line, RATLINE_TYPE));
648 /* ---------------------------------------------------------------------------
649 * writes netlist data
651 static void
652 WritePCBNetlistData (FILE * FP)
654 /* write out the netlist if it exists */
655 if (PCB->NetlistLib.MenuN)
657 int n, p;
658 fprintf (FP, "NetList()\n(\n");
660 for (n = 0; n < PCB->NetlistLib.MenuN; n++)
662 LibraryMenuType *menu = &PCB->NetlistLib.Menu[n];
663 fprintf (FP, "\tNet(");
664 PrintQuotedString(FP, &menu->Name[2]);
665 fprintf (FP, " ");
666 PrintQuotedString(FP, (char *)UNKNOWN (menu->Style));
667 fprintf (FP, ")\n\t(\n");
668 for (p = 0; p < menu->EntryN; p++)
670 LibraryEntryType *entry = &menu->Entry[p];
671 fprintf (FP, "\t\tConnect(");
672 PrintQuotedString (FP, entry->ListEntry);
673 fprintf (FP, ")\n");
675 fprintf (FP, "\t)\n");
677 fprintf (FP, ")\n");
681 /* ---------------------------------------------------------------------------
682 * writes element data
684 static void
685 WriteElementData (FILE * FP, DataType *Data)
687 GList *n, *p;
688 for (n = Data->Element; n != NULL; n = g_list_next (n))
690 ElementType *element = n->data;
692 /* only non empty elements */
693 if (!element->LineN && !element->PinN && !element->ArcN
694 && !element->PadN)
695 continue;
696 /* the coordinates and text-flags are the same for
697 * both names of an element
699 fprintf (FP, "\nElement[%s ", F2S (element, ELEMENT_TYPE));
700 PrintQuotedString (FP, (char *)EMPTY (DESCRIPTION_NAME (element)));
701 fputc (' ', FP);
702 PrintQuotedString (FP, (char *)EMPTY (NAMEONPCB_NAME (element)));
703 fputc (' ', FP);
704 PrintQuotedString (FP, (char *)EMPTY (VALUE_NAME (element)));
705 pcb_fprintf (FP, " %mr %mr %mr %mr %d %d %s]\n(\n",
706 element->MarkX, element->MarkY,
707 DESCRIPTION_TEXT (element).X - element->MarkX,
708 DESCRIPTION_TEXT (element).Y - element->MarkY,
709 DESCRIPTION_TEXT (element).Direction,
710 DESCRIPTION_TEXT (element).Scale,
711 F2S (&(DESCRIPTION_TEXT (element)), ELEMENTNAME_TYPE));
712 WriteAttributeList (FP, &element->Attributes, "\t");
713 for (p = element->Pin; p != NULL; p = g_list_next (p))
715 PinType *pin = p->data;
716 pcb_fprintf (FP, "\tPin[%mr %mr %mr %mr %mr %mr ",
717 pin->X - element->MarkX,
718 pin->Y - element->MarkY,
719 pin->Thickness, pin->Clearance,
720 pin->Mask, pin->DrillingHole);
721 PrintQuotedString (FP, (char *)EMPTY (pin->Name));
722 fprintf (FP, " ");
723 PrintQuotedString (FP, (char *)EMPTY (pin->Number));
724 fprintf (FP, " %s]\n", F2S (pin, PIN_TYPE));
726 for (p = element->Pad; p != NULL; p = g_list_next (p))
728 PadType *pad = p->data;
729 pcb_fprintf (FP, "\tPad[%mr %mr %mr %mr %mr %mr %mr ",
730 pad->Point1.X - element->MarkX,
731 pad->Point1.Y - element->MarkY,
732 pad->Point2.X - element->MarkX,
733 pad->Point2.Y - element->MarkY,
734 pad->Thickness, pad->Clearance, pad->Mask);
735 PrintQuotedString (FP, (char *)EMPTY (pad->Name));
736 fprintf (FP, " ");
737 PrintQuotedString (FP, (char *)EMPTY (pad->Number));
738 fprintf (FP, " %s]\n", F2S (pad, PAD_TYPE));
740 for (p = element->Line; p != NULL; p = g_list_next (p))
742 LineType *line = p->data;
743 pcb_fprintf (FP, "\tElementLine [%mr %mr %mr %mr %mr]\n",
744 line->Point1.X - element->MarkX,
745 line->Point1.Y - element->MarkY,
746 line->Point2.X - element->MarkX,
747 line->Point2.Y - element->MarkY,
748 line->Thickness);
750 for (p = element->Arc; p != NULL; p = g_list_next (p))
752 ArcType *arc = p->data;
753 pcb_fprintf (FP, "\tElementArc [%mr %mr %mr %mr %ma %ma %mr]\n",
754 arc->X - element->MarkX,
755 arc->Y - element->MarkY,
756 arc->Width, arc->Height,
757 arc->StartAngle, arc->Delta,
758 arc->Thickness);
760 fputs ("\n\t)\n", FP);
764 /* ---------------------------------------------------------------------------
765 * writes layer data
767 static void
768 WriteLayerData (FILE * FP, Cardinal Number, LayerType *layer)
770 GList *n;
771 /* write information about non empty layers */
772 if (layer->LineN || layer->ArcN || layer->TextN || layer->PolygonN ||
773 (layer->Name && *layer->Name))
775 fprintf (FP, "Layer(%i ", (int) Number + 1);
776 PrintQuotedString (FP, (char *)EMPTY (layer->Name));
777 fputs (")\n(\n", FP);
778 WriteAttributeList (FP, &layer->Attributes, "\t");
780 for (n = layer->Line; n != NULL; n = g_list_next (n))
782 LineType *line = n->data;
783 pcb_fprintf (FP, "\tLine[%mr %mr %mr %mr %mr %mr %s]\n",
784 line->Point1.X, line->Point1.Y,
785 line->Point2.X, line->Point2.Y,
786 line->Thickness, line->Clearance,
787 F2S (line, LINE_TYPE));
789 for (n = layer->Arc; n != NULL; n = g_list_next (n))
791 ArcType *arc = n->data;
792 pcb_fprintf (FP, "\tArc[%mr %mr %mr %mr %mr %mr %ma %ma %s]\n",
793 arc->X, arc->Y, arc->Width,
794 arc->Height, arc->Thickness,
795 arc->Clearance, arc->StartAngle,
796 arc->Delta, F2S (arc, ARC_TYPE));
798 for (n = layer->Text; n != NULL; n = g_list_next (n))
800 TextType *text = n->data;
801 pcb_fprintf (FP, "\tText[%mr %mr %d %d ",
802 text->X, text->Y,
803 text->Direction, text->Scale);
804 PrintQuotedString (FP, (char *)EMPTY (text->TextString));
805 fprintf (FP, " %s]\n", F2S (text, TEXT_TYPE));
807 for (n = layer->Polygon; n != NULL; n = g_list_next (n))
809 PolygonType *polygon = n->data;
810 int p, i = 0;
811 Cardinal hole = 0;
812 fprintf (FP, "\tPolygon(%s)\n\t(", F2S (polygon, POLYGON_TYPE));
813 for (p = 0; p < polygon->PointN; p++)
815 PointType *point = &polygon->Points[p];
817 if (hole < polygon->HoleIndexN &&
818 p == polygon->HoleIndex[hole])
820 if (hole > 0)
821 fputs ("\n\t\t)", FP);
822 fputs ("\n\t\tHole (", FP);
823 hole++;
824 i = 0;
827 if (i++ % 5 == 0)
829 fputs ("\n\t\t", FP);
830 if (hole)
831 fputs ("\t", FP);
833 pcb_fprintf (FP, "[%mr %mr] ", point->X, point->Y);
835 if (hole > 0)
836 fputs ("\n\t\t)", FP);
837 fputs ("\n\t)\n", FP);
839 fputs (")\n", FP);
843 /* ---------------------------------------------------------------------------
844 * writes just the elements in the buffer to file
846 static int
847 WriteBuffer (FILE * FP)
849 Cardinal i;
851 WriteViaData (FP, PASTEBUFFER->Data);
852 WriteElementData (FP, PASTEBUFFER->Data);
853 for (i = 0; i < max_copper_layer + 2; i++)
854 WriteLayerData (FP, i, &(PASTEBUFFER->Data->Layer[i]));
855 return (STATUS_OK);
858 /* ---------------------------------------------------------------------------
859 * writes PCB to file
861 static int
862 WritePCB (FILE * FP)
864 Cardinal i;
865 if (Settings.SaveMetricOnly)
866 set_allow_readable (ALLOW_MM);
867 else
868 set_allow_readable (ALLOW_READABLE);
870 WritePCBInfoHeader (FP);
871 WritePCBDataHeader (FP);
872 WritePCBFontData (FP);
873 WriteAttributeList (FP, &PCB->Attributes, "");
874 WriteViaData (FP, PCB->Data);
875 WriteElementData (FP, PCB->Data);
876 WritePCBRatData (FP);
877 for (i = 0; i < max_copper_layer + 2; i++)
878 WriteLayerData (FP, i, &(PCB->Data->Layer[i]));
879 WritePCBNetlistData (FP);
881 return (STATUS_OK);
884 /* ---------------------------------------------------------------------------
885 * writes PCB to file
887 static int
888 WritePCBFile (char *Filename)
890 FILE *fp;
891 int result;
893 if ((fp = fopen (Filename, "w")) == NULL)
895 OpenErrorMessage (Filename);
896 return (STATUS_ERROR);
898 result = WritePCB (fp);
899 fclose (fp);
900 return (result);
903 /* ---------------------------------------------------------------------------
904 * writes to pipe using the command defined by Settings.SaveCommand
905 * %f are replaced by the passed filename
907 static int
908 WritePipe (char *Filename, bool thePcb)
910 FILE *fp;
911 int result;
912 char *p;
913 static DynamicStringType command;
914 int used_popen = 0;
916 if (EMPTY_STRING_P (Settings.SaveCommand))
918 fp = fopen (Filename, "w");
919 if (fp == 0)
921 Message ("Unable to write to file %s\n", Filename);
922 return STATUS_ERROR;
925 else
927 used_popen = 1;
928 /* setup commandline */
929 DSClearString (&command);
930 for (p = Settings.SaveCommand; *p; p++)
932 /* copy character if not special or add string to command */
933 if (!(*p == '%' && *(p + 1) == 'f'))
934 DSAddCharacter (&command, *p);
935 else
937 DSAddString (&command, Filename);
939 /* skip the character */
940 p++;
943 DSAddCharacter (&command, '\0');
944 printf ("write to pipe \"%s\"\n", command.Data);
945 if ((fp = popen (command.Data, "w")) == NULL)
947 PopenErrorMessage (command.Data);
948 return (STATUS_ERROR);
951 if (thePcb)
953 if (PCB->is_footprint)
955 WriteElementData (fp, PCB->Data);
956 result = 0;
958 else
959 result = WritePCB (fp);
961 else
962 result = WriteBuffer (fp);
964 if (used_popen)
965 return (pclose (fp) ? STATUS_ERROR : result);
966 return (fclose (fp) ? STATUS_ERROR : result);
969 /* ---------------------------------------------------------------------------
970 * saves the layout in a temporary file
971 * this is used for fatal errors and does not call the program specified
972 * in 'saveCommand' for safety reasons
974 void
975 SaveInTMP (void)
977 char filename[80];
979 /* memory might have been released before this function is called */
980 if (PCB && PCB->Changed)
982 sprintf (filename, EMERGENCY_NAME, (int) getpid ());
983 Message (_("Trying to save your layout in '%s'\n"), filename);
984 WritePCBFile (filename);
988 /* ---------------------------------------------------------------------------
989 * front-end for 'SaveInTMP()'
990 * just makes sure that the routine is only called once
992 static bool dont_save_any_more = false;
993 void
994 EmergencySave (void)
997 if (!dont_save_any_more)
999 SaveInTMP ();
1000 dont_save_any_more = true;
1004 void
1005 DisableEmergencySave (void)
1007 dont_save_any_more = true;
1010 /* ----------------------------------------------------------------------
1011 * Callback for the autosave
1014 static hidval backup_timer;
1017 * If the backup interval is > 0 then set another timer. Otherwise
1018 * we do nothing and it is up to the GUI to call EnableAutosave()
1019 * after setting Settings.BackupInterval > 0 again.
1021 static void
1022 backup_cb (hidval data)
1024 backup_timer.ptr = NULL;
1025 Backup ();
1026 if (Settings.BackupInterval > 0 && gui->add_timer)
1027 backup_timer = gui->add_timer (backup_cb,
1028 1000 * Settings.BackupInterval, data);
1031 void
1032 EnableAutosave (void)
1034 hidval x;
1036 x.ptr = NULL;
1038 /* If we already have a timer going, then cancel it out */
1039 if (backup_timer.ptr != NULL && gui->stop_timer)
1040 gui->stop_timer (backup_timer);
1042 backup_timer.ptr = NULL;
1043 /* Start up a new timer */
1044 if (Settings.BackupInterval > 0 && gui->add_timer)
1045 backup_timer = gui->add_timer (backup_cb,
1046 1000 * Settings.BackupInterval,
1050 /* ---------------------------------------------------------------------------
1051 * creates backup file. The default is to use the pcb file name with
1052 * a "-" appended (like "foo.pcb-") and if we don't have a pcb file name
1053 * then use the template in BACKUP_NAME
1055 void
1056 Backup (void)
1058 char *filename = NULL;
1060 if( PCB && PCB->Filename )
1062 filename = (char *) malloc (sizeof (char) * (strlen (PCB->Filename) + 2));
1063 if (filename == NULL)
1065 fprintf (stderr, "Backup(): malloc failed\n");
1066 exit (1);
1068 sprintf (filename, "%s~", PCB->Filename);
1070 else
1072 /* BACKUP_NAME has %.8i which will be replaced by the process ID */
1073 filename = (char *) malloc (sizeof (char) * (strlen (BACKUP_NAME) + 8));
1074 if (filename == NULL)
1076 fprintf (stderr, "Backup(): malloc failed\n");
1077 exit (1);
1079 sprintf (filename, BACKUP_NAME, (int) getpid ());
1082 WritePCBFile (filename);
1083 free (filename);
1086 #if !defined(HAS_ATEXIT) && !defined(HAS_ON_EXIT)
1087 /* ---------------------------------------------------------------------------
1088 * makes a temporary copy of the data. This is useful for systems which
1089 * doesn't support calling functions on exit. We use this to save the data
1090 * before LEX and YACC functions are called because they are able to abort
1091 * the program.
1093 void
1094 SaveTMPData (void)
1096 sprintf (TMPFilename, EMERGENCY_NAME, (int) getpid ());
1097 WritePCBFile (TMPFilename);
1100 /* ---------------------------------------------------------------------------
1101 * removes the temporary copy of the data file
1103 void
1104 RemoveTMPData (void)
1106 unlink (TMPFilename);
1108 #endif
1110 /* ---------------------------------------------------------------------------
1111 * Parse the directory tree where newlib footprints are found
1114 /* Helper function for ParseLibraryTree */
1115 static char *
1116 pcb_basename (char *p)
1118 char *rv = strrchr (p, '/');
1119 if (rv)
1120 return rv + 1;
1121 return p;
1124 /* This is a helper function for ParseLibrary Tree. Given a char *path,
1125 * it finds all newlib footprints in that dir and sticks them into the
1126 * library menu structure named entry.
1128 static int
1129 LoadNewlibFootprintsFromDir(char *libpath, char *toppath)
1131 char olddir[MAXPATHLEN + 1]; /* The directory we start out in (cwd) */
1132 char subdir[MAXPATHLEN + 1]; /* The directory holding footprints to load */
1133 DIR *subdirobj; /* Interable object holding all subdir entries */
1134 struct dirent *subdirentry; /* Individual subdir entry */
1135 struct stat buffer; /* Buffer used in stat */
1136 LibraryMenuType *menu = NULL; /* Pointer to PCB's library menu structure */
1137 LibraryEntryType *entry; /* Pointer to individual menu entry */
1138 size_t l;
1139 size_t len;
1140 int n_footprints = 0; /* Running count of footprints found in this subdir */
1142 /* Cache old dir, then cd into subdir because stat is given relative file names. */
1143 memset (subdir, 0, sizeof subdir);
1144 memset (olddir, 0, sizeof olddir);
1145 if (GetWorkingDirectory (olddir) == NULL)
1147 Message (_("LoadNewlibFootprintsFromDir: Could not determine initial working directory\n"));
1148 return 0;
1151 if (strcmp (libpath, "(local)") == 0)
1152 strcpy (subdir, ".");
1153 else
1154 strcpy (subdir, libpath);
1156 if (chdir (subdir))
1158 ChdirErrorMessage (subdir);
1159 return 0;
1162 /* Determine subdir is abs path */
1163 if (GetWorkingDirectory (subdir) == NULL)
1165 Message (_("LoadNewlibFootprintsFromDir: Could not determine new working directory\n"));
1166 if (chdir (olddir))
1167 ChdirErrorMessage (olddir);
1168 return 0;
1171 /* First try opening the directory specified by path */
1172 if ( (subdirobj = opendir (subdir)) == NULL )
1174 OpendirErrorMessage (subdir);
1175 if (chdir (olddir))
1176 ChdirErrorMessage (olddir);
1177 return 0;
1180 /* Get pointer to memory holding menu */
1181 menu = GetLibraryMenuMemory (&Library);
1182 /* Populate menuname and path vars */
1183 menu->Name = strdup (pcb_basename(subdir));
1184 menu->directory = strdup (pcb_basename(toppath));
1186 /* Now loop over files in this directory looking for files.
1187 * We ignore certain files which are not footprints.
1189 while ((subdirentry = readdir (subdirobj)) != NULL)
1191 #ifdef DEBUG
1192 /* printf("... Examining file %s ... \n", subdirentry->d_name); */
1193 #endif
1195 /* Ignore non-footprint files found in this directory
1196 * We're skipping .png and .html because those
1197 * may exist in a library tree to provide an html browsable
1198 * index of the library.
1200 l = strlen (subdirentry->d_name);
1201 if (!stat (subdirentry->d_name, &buffer) && S_ISREG (buffer.st_mode)
1202 && subdirentry->d_name[0] != '.'
1203 && NSTRCMP (subdirentry->d_name, "CVS") != 0
1204 && NSTRCMP (subdirentry->d_name, "Makefile") != 0
1205 && NSTRCMP (subdirentry->d_name, "Makefile.am") != 0
1206 && NSTRCMP (subdirentry->d_name, "Makefile.in") != 0
1207 && (l < 4 || NSTRCMP(subdirentry->d_name + (l - 4), ".png") != 0)
1208 && (l < 5 || NSTRCMP(subdirentry->d_name + (l - 5), ".html") != 0)
1209 && (l < 4 || NSTRCMP(subdirentry->d_name + (l - 4), ".pcb") != 0) )
1211 #ifdef DEBUG
1212 /* printf("... Found a footprint %s ... \n", subdirentry->d_name); */
1213 #endif
1214 n_footprints++;
1215 entry = GetLibraryEntryMemory (menu);
1218 * entry->AllocatedMemory points to abs path to the footprint.
1219 * entry->ListEntry points to fp name itself.
1221 len = strlen(subdir) + strlen("/") + strlen(subdirentry->d_name) + 1;
1222 entry->AllocatedMemory = (char *)calloc (1, len);
1223 strcat (entry->AllocatedMemory, subdir);
1224 strcat (entry->AllocatedMemory, PCB_DIR_SEPARATOR_S);
1226 /* store pointer to start of footprint name */
1227 entry->ListEntry = entry->AllocatedMemory
1228 + strlen (entry->AllocatedMemory);
1230 /* Now place footprint name into AllocatedMemory */
1231 strcat (entry->AllocatedMemory, subdirentry->d_name);
1233 /* mark as directory tree (newlib) library */
1234 entry->Template = (char *) -1;
1237 /* Done. Clean up, cd back into old dir, and return */
1238 closedir (subdirobj);
1239 if (chdir (olddir))
1240 ChdirErrorMessage (olddir);
1241 return n_footprints;
1245 /* This function loads the newlib footprints into the Library.
1246 * It examines all directories pointed to by Settings.LibraryTree.
1247 * In each directory specified there, it looks both in that directory,
1248 * as well as *one* level down. It calls the subfunction
1249 * LoadNewlibFootprintsFromDir to put the footprints into PCB's internal
1250 * datastructures.
1252 static int
1253 ParseLibraryTree (void)
1255 char toppath[MAXPATHLEN + 1]; /* String holding abs path to top level library dir */
1256 char working[MAXPATHLEN + 1]; /* String holding abs path to working dir */
1257 char *libpaths; /* String holding list of library paths to search */
1258 char *p; /* Helper string used in iteration */
1259 DIR *dirobj; /* Iterable directory object */
1260 struct dirent *direntry = NULL; /* Object holding individual directory entries */
1261 struct stat buffer; /* buffer used in stat */
1262 int n_footprints = 0; /* Running count of footprints found */
1264 /* Initialize path, working by writing 0 into every byte. */
1265 memset (toppath, 0, sizeof toppath);
1266 memset (working, 0, sizeof working);
1268 /* Save the current working directory as an absolute path.
1269 * This fcn writes the abs path into the memory pointed to by the input arg.
1271 if (GetWorkingDirectory (working) == NULL)
1273 Message (_("ParseLibraryTree: Could not determine initial working directory\n"));
1274 return 0;
1277 /* Additional loop to allow for multiple 'newlib' style library directories
1278 * called out in Settings.LibraryTree
1280 libpaths = strdup (Settings.LibraryTree);
1281 for (p = strtok (libpaths, PCB_PATH_DELIMETER); p && *p; p = strtok (NULL, PCB_PATH_DELIMETER))
1283 /* remove trailing path delimeter */
1284 strncpy (toppath, p, sizeof (toppath) - 1);
1286 /* start out in the working directory in case the path is a
1287 * relative path
1289 if (chdir (working))
1291 ChdirErrorMessage (working);
1292 free (libpaths);
1293 return 0;
1297 * Next change to the directory which is the top of the library tree
1298 * and extract its abs path.
1300 if (chdir (toppath))
1302 ChdirErrorMessage (toppath);
1303 continue;
1306 if (GetWorkingDirectory (toppath) == NULL)
1308 Message (_("ParseLibraryTree: Could not determine new working directory\n"));
1309 continue;
1312 #ifdef DEBUG
1313 printf("In ParseLibraryTree, looking for newlib footprints inside top level directory %s ... \n",
1314 toppath);
1315 #endif
1317 /* Next read in any footprints in the top level dir */
1318 n_footprints += LoadNewlibFootprintsFromDir("(local)", toppath);
1320 /* Then open this dir so we can loop over its contents. */
1321 if ((dirobj = opendir (toppath)) == NULL)
1323 OpendirErrorMessage (toppath);
1324 continue;
1327 /* Now loop over files in this directory looking for subdirs.
1328 * For each direntry which is a valid subdirectory,
1329 * try to load newlib footprints inside it.
1331 while ((direntry = readdir (dirobj)) != NULL)
1333 #ifdef DEBUG
1334 printf("In ParseLibraryTree loop examining 2nd level direntry %s ... \n", direntry->d_name);
1335 #endif
1336 /* Find subdirectories. Ignore entries beginning with "." and CVS
1337 * directories.
1339 if (!stat (direntry->d_name, &buffer)
1340 && S_ISDIR (buffer.st_mode)
1341 && direntry->d_name[0] != '.'
1342 && NSTRCMP (direntry->d_name, "CVS") != 0)
1344 /* Found a valid subdirectory. Try to load footprints from it.
1346 n_footprints += LoadNewlibFootprintsFromDir(direntry->d_name, toppath);
1349 closedir (dirobj);
1352 /* restore the original working directory */
1353 if (chdir (working))
1354 ChdirErrorMessage (working);
1356 #ifdef DEBUG
1357 printf("Leaving ParseLibraryTree, found %d footprints.\n", n_footprints);
1358 #endif
1360 free (libpaths);
1361 return n_footprints;
1364 /* ---------------------------------------------------------------------------
1365 * Read contents of the library description file (for M4)
1366 * and then read in M4 libs. Then call a fcn to read the newlib
1367 * footprints.
1370 ReadLibraryContents (void)
1372 static char *command = NULL;
1373 char inputline[MAX_LIBRARY_LINE_LENGTH + 1];
1374 FILE *resultFP = NULL;
1375 LibraryMenuType *menu = NULL;
1376 LibraryEntryType *entry;
1378 /* If we don't have a command to execute to find the library contents,
1379 * skip this. This is used by default on Windows builds (set in main.c),
1380 * as we can't normally run shell scripts or expect to have m4 present.
1382 if (Settings.LibraryContentsCommand != NULL &&
1383 Settings.LibraryContentsCommand[0] != '\0')
1385 /* First load the M4 stuff. The variable Settings.LibraryPath
1386 * points to it.
1388 free (command);
1389 command = EvaluateFilename (Settings.LibraryContentsCommand,
1390 Settings.LibraryPath, Settings.LibraryFilename,
1391 NULL);
1393 #ifdef DEBUG
1394 printf("In ReadLibraryContents, about to execute command %s\n", command);
1395 #endif
1397 /* This uses a pipe to execute a shell script which provides the names of
1398 * all M4 libs and footprints. The results are placed in resultFP.
1400 if (command && *command && (resultFP = popen (command, "r")) == NULL)
1402 PopenErrorMessage (command);
1405 /* the M4 library contents are separated by colons;
1406 * template : package : name : description
1408 while (resultFP != NULL && fgets (inputline, MAX_LIBRARY_LINE_LENGTH, resultFP))
1410 size_t len = strlen (inputline);
1412 /* check for maximum linelength */
1413 if (len)
1415 len--;
1416 if (inputline[len] != '\n')
1417 Message
1418 ("linelength (%i) exceeded; following characters will be ignored\n",
1419 MAX_LIBRARY_LINE_LENGTH);
1420 else
1421 inputline[len] = '\0';
1424 /* if the line defines a menu */
1425 if (!strncmp (inputline, "TYPE=", 5))
1427 menu = GetLibraryMenuMemory (&Library);
1428 menu->Name = strdup (UNKNOWN (&inputline[5]));
1429 menu->directory = strdup (Settings.LibraryFilename);
1431 else
1433 /* allocate a new menu entry if not already done */
1434 if (!menu)
1436 menu = GetLibraryMenuMemory (&Library);
1437 menu->Name = strdup (UNKNOWN ((char *) NULL));
1438 menu->directory = strdup (Settings.LibraryFilename);
1440 entry = GetLibraryEntryMemory (menu);
1441 entry->AllocatedMemory = strdup (inputline);
1443 /* now break the line into pieces separated by colons */
1444 if ((entry->Template = strtok (entry->AllocatedMemory, ":")) !=
1445 NULL)
1446 if ((entry->Package = strtok (NULL, ":")) != NULL)
1447 if ((entry->Value = strtok (NULL, ":")) != NULL)
1448 entry->Description = strtok (NULL, ":");
1450 /* create the list entry */
1451 len = strlen (EMPTY (entry->Value)) +
1452 strlen (EMPTY (entry->Description)) + 4;
1453 entry->ListEntry = (char *)calloc (len, sizeof (char));
1454 sprintf (entry->ListEntry,
1455 "%s, %s", EMPTY (entry->Value),
1456 EMPTY (entry->Description));
1459 if (resultFP != NULL)
1460 pclose (resultFP);
1463 /* Now after reading in the M4 libs, call a function to
1464 * read the newlib footprint libraries. Then sort the whole
1465 * library.
1467 if (ParseLibraryTree () > 0 || resultFP != NULL)
1469 sort_library (&Library);
1470 return 0;
1473 return (1);
1476 #define BLANK(x) ((x) == ' ' || (x) == '\t' || (x) == '\n' \
1477 || (x) == '\0')
1479 /* ---------------------------------------------------------------------------
1480 * Read in a netlist and store it in the netlist menu
1484 ReadNetlist (char *filename)
1486 static char *command = NULL;
1487 char inputline[MAX_NETLIST_LINE_LENGTH + 1];
1488 char temp[MAX_NETLIST_LINE_LENGTH + 1];
1489 FILE *fp;
1490 LibraryMenuType *menu = NULL;
1491 LibraryEntryType *entry;
1492 int i, j, lines, kind;
1493 bool continued;
1494 bool used_popen = false;
1495 int retval = 0;
1497 if (!filename)
1498 return 1; /* nothing to do */
1500 Message (_("Importing PCB netlist %s\n"), filename);
1502 if (EMPTY_STRING_P (Settings.RatCommand))
1504 fp = fopen (filename, "r");
1505 if (!fp)
1507 Message("Cannot open %s for reading", filename);
1508 return 1;
1511 else
1513 used_popen = true;
1514 free (command);
1515 command = EvaluateFilename (Settings.RatCommand,
1516 Settings.RatPath, filename, NULL);
1518 /* open pipe to stdout of command */
1519 if (*command == '\0' || (fp = popen (command, "r")) == NULL)
1521 PopenErrorMessage (command);
1522 return 1;
1525 lines = 0;
1526 /* kind = 0 is net name
1527 * kind = 1 is route style name
1528 * kind = 2 is connection
1530 kind = 0;
1531 while (fgets (inputline, MAX_NETLIST_LINE_LENGTH, fp))
1533 size_t len = strlen (inputline);
1534 /* check for maximum length line */
1535 if (len)
1537 if (inputline[--len] != '\n')
1538 Message (_("Line length (%i) exceeded in netlist file.\n"
1539 "additional characters will be ignored.\n"),
1540 MAX_NETLIST_LINE_LENGTH);
1541 else
1542 inputline[len] = '\0';
1544 continued = (inputline[len - 1] == '\\') ? true : false;
1545 if (continued)
1546 inputline[len - 1] = '\0';
1547 lines++;
1548 i = 0;
1549 while (inputline[i] != '\0')
1551 j = 0;
1552 /* skip leading blanks */
1553 while (inputline[i] != '\0' && BLANK (inputline[i]))
1554 i++;
1555 if (kind == 0)
1557 /* add two spaces for included/unincluded */
1558 temp[j++] = ' ';
1559 temp[j++] = ' ';
1561 while (!BLANK (inputline[i]))
1562 temp[j++] = inputline[i++];
1563 temp[j] = '\0';
1564 while (inputline[i] != '\0' && BLANK (inputline[i]))
1565 i++;
1566 if (kind == 0)
1568 menu = GetLibraryMenuMemory (&PCB->NetlistLib);
1569 menu->Name = strdup (temp);
1570 menu->flag = 1;
1571 kind++;
1573 else
1575 if (kind == 1 && strchr (temp, '-') == NULL)
1577 kind++;
1578 menu->Style = strdup (temp);
1580 else
1582 entry = GetLibraryEntryMemory (menu);
1583 entry->ListEntry = strdup (temp);
1587 if (!continued)
1588 kind = 0;
1590 if (!lines)
1592 Message (_("Empty netlist file!\n"));
1593 retval = 1;
1595 if (used_popen)
1596 pclose (fp);
1597 else
1598 fclose (fp);
1599 sort_netlist ();
1600 return retval;
1603 static int ReadEdifNetlist (char *filename);
1605 int ImportNetlist (char *filename)
1607 FILE *fp;
1608 char buf[16];
1609 int i;
1610 char* p;
1613 if (!filename) return (1); /* nothing to do */
1614 fp = fopen (filename, "r");
1615 if (!fp) return (1); /* bad filename */
1616 i = fread (buf, 1, sizeof(buf)-1, fp);
1617 fclose(fp);
1618 buf[i] = '\0';
1619 p=buf;
1620 while ( *p )
1622 *p = tolower ((int) *p);
1623 p++;
1625 p = strstr (buf, "edif");
1626 if (!p) return ReadNetlist (filename);
1627 else return ReadEdifNetlist (filename);
1630 static int ReadEdifNetlist (char *filename)
1632 Message (_("Importing edif netlist %s\n"), filename);
1633 ParseEDIF(filename, NULL);
1635 return 0;