pcb 4.1.1
An interactive printed circuit board layout editor.

file.c

Go to the documentation of this file.
00001 
00036 #ifdef HAVE_CONFIG_H
00037 #include "config.h"
00038 #endif
00039 
00040 #ifdef HAVE_SYS_PARAM_H
00041 #include <sys/param.h>
00042 #endif
00043 
00044 #include "global.h"
00045 
00046 #include <dirent.h>
00047 #ifdef HAVE_PWD_H
00048 #include <pwd.h>
00049 #endif
00050 #include <time.h>
00051 
00052 #ifdef HAVE_SYS_SOCKET_H
00053 #include <sys/socket.h>
00054 #endif
00055 
00056 #include <sys/stat.h>
00057 
00058 #ifdef HAVE_NETINET_IN_H
00059 #include <netinet/in.h>
00060 #endif
00061 
00062 #ifdef HAVE_NETDB_H
00063 #include <netdb.h>
00064 #endif
00065 
00066 #include <stdio.h>
00067 
00068 #ifdef HAVE_STDLIB_H
00069 #include <stdlib.h>
00070 #endif
00071 
00072 #ifdef HAVE_STRING_H
00073 #include <string.h>
00074 #endif
00075 
00076 #ifdef HAVE_UNISTD_H
00077 #include <unistd.h>
00078 #endif
00079 
00080 
00081 #include "buffer.h"
00082 #include "change.h"
00083 #include "create.h"
00084 #include "crosshair.h"
00085 #include "data.h"
00086 #include "edif_parse.h"
00087 #include "error.h"
00088 #include "file.h"
00089 #include "hid.h"
00090 #include "layerflags.h"
00091 #include "misc.h"
00092 #include "mymem.h"
00093 #include "parse_l.h"
00094 #include "pcb-printf.h"
00095 #include "polygon.h"
00096 #include "rats.h"
00097 #include "remove.h"
00098 #include "set.h"
00099 #include "strflags.h"
00100 
00101 #ifdef HAVE_LIBDMALLOC
00102 #include <dmalloc.h>
00103 #endif
00104 
00105 #if !defined(HAS_ATEXIT) && !defined(HAS_ON_EXIT)
00106 /* ---------------------------------------------------------------------------
00107  * some local identifiers for OS without an atexit() or on_exit()
00108  * call
00109  */
00110 static char TMPFilename[80];
00111 #endif
00112 
00113 /* ---------------------------------------------------------------------------
00114  * some local prototypes
00115  */
00116 static void PrintQuotedString (FILE *, char *);
00117 static void WritePCBInfoHeader (FILE *);
00118 static void WritePCBDataHeader (FILE *);
00119 static void WritePCBFontData (FILE *);
00120 static void WriteViaData (FILE *, DataType *);
00121 static void WritePCBRatData (FILE *);
00122 static void WriteElementData (FILE *, DataType *);
00123 static void WriteLayerData (FILE *, Cardinal, LayerType *);
00124 static int WritePCB (FILE *);
00125 static int WritePCBFile (char *);
00126 static int WritePipe (char *, bool);
00127 static int ParseLibraryTree (void);
00128 static int LoadNewlibFootprintsFromDir(char *path, char *toppath, bool recursive);
00129 
00130 /* ---------------------------------------------------------------------------
00131  * Flag helper functions
00132  */
00133 
00134 #define F2S(OBJ, TYPE) flags_to_string ((OBJ)->Flags, TYPE)
00135 
00136 /* --------------------------------------------------------------------------- */
00137 
00138 /* The idea here is to avoid gratuitously breaking backwards
00139    compatibility due to a new but rarely used feature.  The first such
00140    case, for example, was the polygon Hole - if your design included
00141    polygon holes, you needed a newer PCB to read it, but if your
00142    design didn't include holes, PCB would produce a file that older
00143    PCBs could read, if only it had the correct version number in it.
00144 
00145    If, however, you have to add or change a feature that really does
00146    require a new PCB version all the time, it's time to remove all the
00147    tests below and just always output the new version.
00148 
00149    Note: Best practices here is to add support for a feature *first*
00150    (and bump PCB_FILE_VERSION in file.h), and note the version that
00151    added that support below, and *later* update the file format to
00152    need that version (which may then be older than PCB_FILE_VERSION).
00153    Hopefully, that allows for one release between adding support and
00154    needing it, which should minimize breakage.  Of course, that's not
00155    *always* possible, practical, or desirable.
00156 
00157 */
00158 
00159 
00160 #define PCB_FILE_VERSION_BURIED_VIAS 20170218 
00162 #define PCB_FILE_VERSION_HOLES 20100606 
00164 #define PCB_FILE_VERSION_BASELINE 20091103 
00166 int
00167 PCBFileVersionNeeded (void)
00168 {
00169   ALLPOLYGON_LOOP (PCB->Data);
00170   {
00171     if (polygon->HoleIndexN > 0)
00172       return PCB_FILE_VERSION_HOLES;
00173   }
00174   ENDALL_LOOP;
00175 
00176   VIA_LOOP (PCB->Data);
00177     if ((via->BuriedFrom != 0) || (via->BuriedTo != 0))
00178       return PCB_FILE_VERSION_BURIED_VIAS;
00179   END_LOOP;
00180 
00181   return PCB_FILE_VERSION_BASELINE;
00182 }
00183 
00184 /* --------------------------------------------------------------------------- */
00185 
00186 static int
00187 string_cmp (const char *a, const char *b)
00188 {
00189   while (*a && *b)
00190     {
00191       if (isdigit ((int) *a) && isdigit ((int) *b))
00192         {
00193           int ia = atoi (a);
00194           int ib = atoi (b);
00195           if (ia != ib)
00196             return ia - ib;
00197           while (isdigit ((int) *a) && *(a+1))
00198             a++;
00199           while (isdigit ((int) *b) && *(b+1))
00200             b++;
00201         }
00202       else if (tolower ((int) *a) != tolower ((int) *b))
00203         return tolower ((int) *a) - tolower ((int) *b);
00204       a++;
00205       b++;
00206     }
00207   if (*a)
00208     return 1;
00209   if (*b)
00210     return -1;
00211   return 0;
00212 }
00213 
00214 static int netlist_sort_offset = 0;
00215 
00216 static int
00217 netlist_sort (const void *va, const void *vb)
00218 {
00219   LibraryMenuType *am = (LibraryMenuType *) va;
00220   LibraryMenuType *bm = (LibraryMenuType *) vb;
00221   char *a = am->Name;
00222   char *b = bm->Name;
00223   if (*a == '~')
00224     a++;
00225   if (*b == '~')
00226     b++;
00227   return string_cmp (a, b);
00228 }
00229 
00230 static int
00231 netnode_sort (const void *va, const void *vb)
00232 {
00233   LibraryEntryType *am = (LibraryEntryType *) va;
00234   LibraryEntryType *bm = (LibraryEntryType *) vb;
00235   char *a = am->ListEntry;
00236   char *b = bm->ListEntry;
00237   return string_cmp (a, b);
00238 }
00239 
00240 static void
00241 sort_library (LibraryType *lib)
00242 {
00243   int i;
00244   qsort (lib->Menu, lib->MenuN, sizeof (lib->Menu[0]), netlist_sort);
00245   for (i = 0; i < lib->MenuN; i++)
00246     qsort (lib->Menu[i].Entry,
00247            lib->Menu[i].EntryN, sizeof (lib->Menu[i].Entry[0]), netnode_sort);
00248 }
00249 
00250 void
00251 sort_netlist ()
00252 {
00253   netlist_sort_offset = 2;
00254   sort_library (&(PCB->NetlistLib));
00255   netlist_sort_offset = 0;
00256 }
00257 
00261 FILE *
00262 CheckAndOpenFile (char *Filename, bool Confirm, bool AllButton,
00263                   bool * WasAllButton, bool * WasCancelButton)
00264 {
00265   FILE *fp = NULL;
00266   struct stat buffer;
00267   char message[MAXPATHLEN + 80];
00268   int response;
00269 
00270   if (Filename && *Filename)
00271     {
00272       if (!stat (Filename, &buffer) && Confirm)
00273         {
00274           sprintf (message, _("File '%s' exists, use anyway?"), Filename);
00275           if (WasAllButton)
00276             *WasAllButton = false;
00277           if (WasCancelButton)
00278             *WasCancelButton = false;
00279           if (AllButton)
00280             response =
00281               gui->confirm_dialog (message, "Cancel", "Ok",
00282                                    AllButton ? "Sequence OK" : 0);
00283           else
00284             response =
00285               gui->confirm_dialog (message, "Cancel", "Ok", "Sequence OK");
00286 
00287           switch (response)
00288             {
00289             case 2:
00290               if (WasAllButton)
00291                 *WasAllButton = true;
00292               break;
00293             case 0:
00294               if (WasCancelButton)
00295                 *WasCancelButton = true;
00296             }
00297         }
00298       if ((fp = fopen (Filename, "w")) == NULL)
00299         OpenErrorMessage (Filename);
00300     }
00301   return (fp);
00302 }
00303 
00307 FILE *
00308 OpenConnectionDataFile (void)
00309 {
00310   char *fname;
00311   FILE *fp;
00312   static char * default_file = NULL;
00313   bool result;          /* not used */
00314 
00315   /* CheckAndOpenFile deals with the case where fname already exists */
00316   fname = gui->fileselect (_("Save Connection Data As ..."),
00317                            _("Choose a file to save all connection data to."),
00318                            default_file, ".net", "connection_data",
00319                            0);
00320   if (fname == NULL)
00321     return NULL;
00322 
00323   if (default_file != NULL)
00324     {
00325       free (default_file);
00326       default_file = NULL;
00327     }
00328 
00329   if (fname && *fname)
00330     default_file = strdup (fname);
00331 
00332   fp = CheckAndOpenFile (fname, true, false, &result, NULL);
00333   free (fname);
00334 
00335   return fp;
00336 }
00337 
00341 int
00342 SaveBufferElements (char *Filename)
00343 {
00344   int result;
00345 
00346   if (SWAP_IDENT)
00347     SwapBuffers ();
00348   result = WritePipe (Filename, false);
00349   if (SWAP_IDENT)
00350     SwapBuffers ();
00351   return (result);
00352 }
00353 
00357 int
00358 SavePCB (char *file)
00359 {
00360   int retcode;
00361 
00362   if (gui->notify_save_pcb == NULL)
00363     return WritePipe (file, true);
00364 
00365   gui->notify_save_pcb (file, false);
00366   retcode = WritePipe (file, true);
00367   gui->notify_save_pcb (file, true);
00368 
00369   return retcode;
00370 }
00371 
00378 static void
00379 set_some_route_style ()
00380 {
00381   if (hid_get_flag ("style"))
00382     return;
00383   SetLineSize (PCB->RouteStyle[0].Thick);
00384   SetViaSize (PCB->RouteStyle[0].Diameter, true);
00385   SetViaDrillingHole (PCB->RouteStyle[0].Hole, true);
00386   SetKeepawayWidth (PCB->RouteStyle[0].Keepaway);
00387 }
00388 
00398 static int
00399 real_load_pcb (char *Filename, bool revert)
00400 {
00401   const char *unit_suffix, *grid_size;
00402   char *new_filename;
00403   PCBType *newPCB = CreateNewPCB ();
00404   PCBType *oldPCB;
00405 #ifdef DEBUG
00406   double elapsed;
00407   clock_t start, end;
00408 
00409   start = clock ();
00410 #endif
00411 
00412   new_filename = strdup (Filename);
00413 
00414   oldPCB = PCB;
00415   PCB = newPCB;
00416 
00417   /* mark the default font invalid to know if the file has one */
00418   newPCB->Font.Valid = false;
00419 
00420   /* new data isn't added to the undo list */
00421   if (!ParsePCB (PCB, new_filename))
00422     {
00423       RemovePCB (oldPCB);
00424 
00425       CreateNewPCBPost (PCB, 0);
00426       ResetStackAndVisibility ();
00427       AssignDefaultLayerTypes();
00428 
00429       /* update cursor location */
00430       Crosshair.X = CLAMP (PCB->CursorX, 0, PCB->MaxWidth);
00431       Crosshair.Y = CLAMP (PCB->CursorY, 0, PCB->MaxHeight);
00432 
00433       /* update cursor confinement and output area (scrollbars) */
00434       ChangePCBSize (PCB->MaxWidth, PCB->MaxHeight);
00435 
00436       /* enable default font if necessary */
00437       if (!PCB->Font.Valid)
00438         {
00439           Message (_
00440                    ("File '%s' has no font information, using default font\n"),
00441                    new_filename);
00442           PCB->Font.Valid = true;
00443         }
00444 
00445       /* clear 'changed flag' */
00446       SetChangedFlag (false);
00447       PCB->Filename = new_filename;
00448       /* just in case a bad file saved file is loaded */
00449 
00450       /* Use attribute PCB::grid::unit as unit, if we can */
00451       unit_suffix = AttributeGet (PCB, "PCB::grid::unit");
00452       if (unit_suffix && *unit_suffix)
00453         {
00454           const Unit *new_unit = get_unit_struct (unit_suffix);
00455           if (new_unit)
00456             Settings.grid_unit = new_unit;
00457         }
00458       AttributePut (PCB, "PCB::grid::unit", Settings.grid_unit->suffix);
00459       Settings.increments = get_increments_struct (Settings.grid_unit->family);
00460 
00461       /* Use attribute PCB::grid::size as size, if we can */
00462       grid_size = AttributeGet (PCB, "PCB::grid::size");
00463       if (grid_size)
00464         {
00465           PCB->Grid = GetValue (grid_size, NULL, NULL);
00466         }
00467  
00468       sort_netlist ();
00469 
00470       set_some_route_style ();
00471 
00472       if (revert)
00473         hid_actionl ("PCBChanged", "revert", NULL);
00474       else
00475         hid_action ("PCBChanged");
00476 
00477 #ifdef DEBUG
00478       end = clock ();
00479       elapsed = ((double) (end - start)) / CLOCKS_PER_SEC;
00480       gui->log ("Loading file %s took %f seconds of CPU time\n",
00481                 new_filename, elapsed);
00482 #endif
00483 
00484       return (0);
00485     }
00486   PCB = oldPCB;
00487   hid_action ("PCBChanged");
00488 
00489   /* release unused memory */
00490   RemovePCB (newPCB);
00491   return (1);
00492 }
00493 
00497 int
00498 LoadPCB (char *file)
00499 {
00500   return real_load_pcb (file, false);
00501 }
00502 
00506 int
00507 RevertPCB (void)
00508 {
00509   return real_load_pcb (PCB->Filename, true);
00510 }
00511 
00515 static void
00516 PrintQuotedString (FILE * FP, char *S)
00517 {
00518   static DynamicStringType ds;
00519 
00520   CreateQuotedString (&ds, S);
00521   fputs (ds.Data, FP);
00522 }
00523 
00527 static void
00528 WriteAttributeList (FILE * FP, AttributeListType *list, char *prefix)
00529 {
00530   int i;
00531 
00532   for (i = 0; i < list->Number; i++)
00533   {
00534     fprintf (FP, "%sAttribute(", prefix);
00535     PrintQuotedString(FP, list->List[i].name);
00536     fputs (" ", FP);
00537     PrintQuotedString(FP, list->List[i].value);
00538     fputs (")\n", FP);
00539   }
00540 }
00541 
00545 static void
00546 WritePCBInfoHeader (FILE * FP)
00547 {
00548   /* write some useful comments */
00549   fprintf (FP, "# release: %s " VERSION "\n", Progname);
00550 
00551   /* avoid writing things like user name or date, as these cause merge
00552    * conflicts in collaborative environments using version control systems
00553    */
00554 }
00555 
00562 static void
00563 WritePCBDataHeader (FILE * FP)
00564 {
00565   Cardinal group;
00566 
00567   /*
00568    * ************************** README *******************
00569    * ************************** README *******************
00570    *
00571    * If the file format is modified in any way, update
00572    * PCB_FILE_VERSION in file.h as well as PCBFileVersionNeeded()
00573    * at the top of this file.
00574    *  
00575    * ************************** README *******************
00576    * ************************** README *******************
00577    */
00578 
00579   fprintf (FP, "\n# To read pcb files, the pcb version (or the git source date) must be >= the file version\n");
00580   fprintf (FP, "FileVersion[%i]\n", PCBFileVersionNeeded ());
00581 
00582   fputs ("\nPCB[", FP);
00583   PrintQuotedString (FP, (char *)EMPTY (PCB->Name));
00584   pcb_fprintf (FP, " %mr %mr]\n\n", PCB->MaxWidth, PCB->MaxHeight);
00585   pcb_fprintf (FP, "Grid[%mr %mr %mr %d]\n", PCB->Grid,
00586                PCB->GridOffsetX, PCB->GridOffsetY, Settings.DrawGrid);
00587   /* PolyArea should be output in square cmils, no suffix */
00588   fprintf (FP, "PolyArea[%s]\n", c_dtostr (COORD_TO_MIL (COORD_TO_MIL (PCB->IsleArea) * 100) * 100));
00589   pcb_fprintf (FP, "Thermal[%s]\n", c_dtostr (PCB->ThermScale));
00590   pcb_fprintf (FP, "DRC[%mr %mr %mr %mr %mr %mr]\n", PCB->Bloat, PCB->Shrink,
00591                PCB->minWid, PCB->minSlk, PCB->minDrill, PCB->minRing);
00592   fprintf (FP, "Flags(%s)\n", pcbflags_to_string(PCB->Flags));
00593   fprintf (FP, "Groups(\"%s\")\n", LayerGroupsToString (&PCB->LayerGroups));
00594   fputs ("Styles[\"", FP);
00595   for (group = 0; group < NUM_STYLES - 1; group++)
00596     pcb_fprintf (FP, "%s,%mr,%mr,%mr,%mr:", PCB->RouteStyle[group].Name,
00597                  PCB->RouteStyle[group].Thick,
00598                  PCB->RouteStyle[group].Diameter,
00599                  PCB->RouteStyle[group].Hole, PCB->RouteStyle[group].Keepaway);
00600   pcb_fprintf (FP, "%s,%mr,%mr,%mr,%mr\"]\n\n", PCB->RouteStyle[group].Name,
00601                PCB->RouteStyle[group].Thick,
00602                PCB->RouteStyle[group].Diameter,
00603                PCB->RouteStyle[group].Hole, PCB->RouteStyle[group].Keepaway);
00604 }
00605 
00609 static void
00610 WritePCBFontData (FILE * FP)
00611 {
00612   Cardinal i, j;
00613   LineType *line;
00614   FontType *font;
00615 
00616   for (font = &PCB->Font, i = 0; i <= MAX_FONTPOSITION; i++)
00617     {
00618       if (!font->Symbol[i].Valid)
00619         continue;
00620 
00621       if (isprint (i))
00622         pcb_fprintf (FP, "Symbol['%c' %mr]\n(\n", i, font->Symbol[i].Delta);
00623       else
00624         pcb_fprintf (FP, "Symbol[%i %mr]\n(\n", i, font->Symbol[i].Delta);
00625 
00626       line = font->Symbol[i].Line;
00627       for (j = font->Symbol[i].LineN; j; j--, line++)
00628         pcb_fprintf (FP, "\tSymbolLine[%mr %mr %mr %mr %mr]\n",
00629                      line->Point1.X, line->Point1.Y,
00630                      line->Point2.X, line->Point2.Y, line->Thickness);
00631       fputs (")\n", FP);
00632     }
00633 }
00634 
00638 static void
00639 WriteViaData (FILE * FP, DataType *Data)
00640 {
00641   GList *iter;
00642   /* write information about vias */
00643   for (iter = Data->Via; iter != NULL; iter = g_list_next (iter))
00644     {
00645       PinType *via = iter->data;
00646       pcb_fprintf (FP, "Via[%mr %mr %mr %mr %mr %mr ", via->X, via->Y,
00647                    via->Thickness, via->Clearance, via->Mask, via->DrillingHole);
00648       if ((via->BuriedFrom != 0) || (via->BuriedTo != 0))
00649         fprintf (FP, "%d %d ", via->BuriedFrom, via->BuriedTo);
00650       PrintQuotedString (FP, (char *)EMPTY (via->Name));
00651       fprintf (FP, " %s]\n", F2S (via, VIA_TYPE));
00652     }
00653 }
00654 
00658 static void
00659 WritePCBRatData (FILE * FP)
00660 {
00661   GList *iter;
00662   /* write information about rats */
00663   for (iter = PCB->Data->Rat; iter != NULL; iter = g_list_next (iter))
00664     {
00665       RatType *line = iter->data;
00666       pcb_fprintf (FP, "Rat[%mr %mr %d %mr %mr %d ",
00667                    line->Point1.X, line->Point1.Y, line->group1,
00668                    line->Point2.X, line->Point2.Y, line->group2);
00669       fprintf (FP, " %s]\n", F2S (line, RATLINE_TYPE));
00670     }
00671 }
00672 
00676 static void
00677 WritePCBNetlistData (FILE * FP)
00678 {
00679   /* write out the netlist if it exists */
00680   if (PCB->NetlistLib.MenuN)
00681     {
00682       int n, p;
00683       fprintf (FP, "NetList()\n(\n");
00684 
00685       for (n = 0; n < PCB->NetlistLib.MenuN; n++)
00686         {
00687           LibraryMenuType *menu = &PCB->NetlistLib.Menu[n];
00688           fprintf (FP, "\tNet(");
00689           PrintQuotedString(FP, &menu->Name[2]);
00690           fprintf (FP, " ");
00691           PrintQuotedString(FP, (char *)UNKNOWN (menu->Style));
00692           fprintf (FP, ")\n\t(\n");
00693           for (p = 0; p < menu->EntryN; p++)
00694             {
00695               LibraryEntryType *entry = &menu->Entry[p];
00696               fprintf (FP, "\t\tConnect(");
00697               PrintQuotedString (FP, entry->ListEntry);
00698               fprintf (FP, ")\n");
00699             }
00700           fprintf (FP, "\t)\n");
00701         }
00702       fprintf (FP, ")\n");
00703     }
00704 }
00705 
00709 static void
00710 WriteElementData (FILE * FP, DataType *Data)
00711 {
00712   GList *n, *p;
00713   for (n = Data->Element; n != NULL; n = g_list_next (n))
00714     {
00715       ElementType *element = n->data;
00716 
00717       /* only non empty elements */
00718       if (!element->LineN && !element->PinN && !element->ArcN
00719           && !element->PadN)
00720         continue;
00721       /* the coordinates and text-flags are the same for
00722        * both names of an element
00723        */
00724       fprintf (FP, "\nElement[%s ", F2S (element, ELEMENT_TYPE));
00725       PrintQuotedString (FP, (char *)EMPTY (DESCRIPTION_NAME (element)));
00726       fputc (' ', FP);
00727       PrintQuotedString (FP, (char *)EMPTY (NAMEONPCB_NAME (element)));
00728       fputc (' ', FP);
00729       PrintQuotedString (FP, (char *)EMPTY (VALUE_NAME (element)));
00730       pcb_fprintf (FP, " %mr %mr %mr %mr %d %d %s]\n(\n",
00731                    element->MarkX, element->MarkY,
00732                    DESCRIPTION_TEXT (element).X - element->MarkX,
00733                    DESCRIPTION_TEXT (element).Y - element->MarkY,
00734                    DESCRIPTION_TEXT (element).Direction,
00735                    DESCRIPTION_TEXT (element).Scale,
00736                    F2S (&(DESCRIPTION_TEXT (element)), ELEMENTNAME_TYPE));
00737       WriteAttributeList (FP, &element->Attributes, "\t");
00738       for (p = element->Pin; p != NULL; p = g_list_next (p))
00739         {
00740           PinType *pin = p->data;
00741           pcb_fprintf (FP, "\tPin[%mr %mr %mr %mr %mr %mr ",
00742                        pin->X - element->MarkX,
00743                        pin->Y - element->MarkY,
00744                        pin->Thickness, pin->Clearance,
00745                        pin->Mask, pin->DrillingHole);
00746           PrintQuotedString (FP, (char *)EMPTY (pin->Name));
00747           fprintf (FP, " ");
00748           PrintQuotedString (FP, (char *)EMPTY (pin->Number));
00749           fprintf (FP, " %s]\n", F2S (pin, PIN_TYPE));
00750         }
00751       for (p = element->Pad; p != NULL; p = g_list_next (p))
00752         {
00753           PadType *pad = p->data;
00754           pcb_fprintf (FP, "\tPad[%mr %mr %mr %mr %mr %mr %mr ",
00755                        pad->Point1.X - element->MarkX,
00756                        pad->Point1.Y - element->MarkY,
00757                        pad->Point2.X - element->MarkX,
00758                        pad->Point2.Y - element->MarkY,
00759                        pad->Thickness, pad->Clearance, pad->Mask);
00760           PrintQuotedString (FP, (char *)EMPTY (pad->Name));
00761           fprintf (FP, " ");
00762           PrintQuotedString (FP, (char *)EMPTY (pad->Number));
00763           fprintf (FP, " %s]\n", F2S (pad, PAD_TYPE));
00764         }
00765       for (p = element->Line; p != NULL; p = g_list_next (p))
00766         {
00767           LineType *line = p->data;
00768           pcb_fprintf (FP, "\tElementLine [%mr %mr %mr %mr %mr]\n",
00769                        line->Point1.X - element->MarkX,
00770                        line->Point1.Y - element->MarkY,
00771                        line->Point2.X - element->MarkX,
00772                        line->Point2.Y - element->MarkY,
00773                        line->Thickness);
00774         }
00775       for (p = element->Arc; p != NULL; p = g_list_next (p))
00776         {
00777           ArcType *arc = p->data;
00778           pcb_fprintf (FP, "\tElementArc [%mr %mr %mr %mr %ma %ma %mr]\n",
00779                        arc->X - element->MarkX,
00780                        arc->Y - element->MarkY,
00781                        arc->Width, arc->Height,
00782                        arc->StartAngle, arc->Delta,
00783                        arc->Thickness);
00784         }
00785       fputs ("\n\t)\n", FP);
00786     }
00787 }
00788 
00792 static void
00793 WriteLayerData (FILE * FP, Cardinal Number, LayerType *layer)
00794 {
00795   GList *n;
00796   /* write information about non empty layers */
00797   if (layer->LineN || layer->ArcN || layer->TextN || layer->PolygonN ||
00798       (layer->Name && *layer->Name))
00799     {
00800       fprintf (FP, "Layer(%i ", (int) Number + 1);
00801       PrintQuotedString (FP, (char *)EMPTY (layer->Name));
00802       fprintf (FP, " \"%s\")\n(\n", layertype_to_string (layer->Type));
00803       WriteAttributeList (FP, &layer->Attributes, "\t");
00804 
00805       for (n = layer->Line; n != NULL; n = g_list_next (n))
00806         {
00807           LineType *line = n->data;
00808           pcb_fprintf (FP, "\tLine[%mr %mr %mr %mr %mr %mr %s]\n",
00809                        line->Point1.X, line->Point1.Y,
00810                        line->Point2.X, line->Point2.Y,
00811                        line->Thickness, line->Clearance,
00812                        F2S (line, LINE_TYPE));
00813         }
00814       for (n = layer->Arc; n != NULL; n = g_list_next (n))
00815         {
00816           ArcType *arc = n->data;
00817           pcb_fprintf (FP, "\tArc[%mr %mr %mr %mr %mr %mr %ma %ma %s]\n",
00818                        arc->X, arc->Y, arc->Width,
00819                        arc->Height, arc->Thickness,
00820                        arc->Clearance, arc->StartAngle,
00821                        arc->Delta, F2S (arc, ARC_TYPE));
00822         }
00823       for (n = layer->Text; n != NULL; n = g_list_next (n))
00824         {
00825           TextType *text = n->data;
00826           pcb_fprintf (FP, "\tText[%mr %mr %d %d ",
00827                        text->X, text->Y,
00828                        text->Direction, text->Scale);
00829           PrintQuotedString (FP, (char *)EMPTY (text->TextString));
00830           fprintf (FP, " %s]\n", F2S (text, TEXT_TYPE));
00831         }
00832       for (n = layer->Polygon; n != NULL; n = g_list_next (n))
00833         {
00834           PolygonType *polygon = n->data;
00835           int p, i = 0;
00836           Cardinal hole = 0;
00837           fprintf (FP, "\tPolygon(%s)\n\t(", F2S (polygon, POLYGON_TYPE));
00838           for (p = 0; p < polygon->PointN; p++)
00839             {
00840               PointType *point = &polygon->Points[p];
00841 
00842               if (hole < polygon->HoleIndexN &&
00843                   p == polygon->HoleIndex[hole])
00844                 {
00845                   if (hole > 0)
00846                     fputs ("\n\t\t)", FP);
00847                   fputs ("\n\t\tHole (", FP);
00848                   hole++;
00849                   i = 0;
00850                 }
00851 
00852               if (i++ % 5 == 0)
00853                 {
00854                   fputs ("\n\t\t", FP);
00855                   if (hole)
00856                     fputs ("\t", FP);
00857                 }
00858               pcb_fprintf (FP, "[%mr %mr] ", point->X, point->Y);
00859             }
00860           if (hole > 0)
00861             fputs ("\n\t\t)", FP);
00862           fputs ("\n\t)\n", FP);
00863         }
00864       fputs (")\n", FP);
00865     }
00866 }
00867 
00871 static int
00872 WriteBuffer (FILE * FP)
00873 {
00874   Cardinal i;
00875 
00876   WriteViaData (FP, PASTEBUFFER->Data);
00877   WriteElementData (FP, PASTEBUFFER->Data);
00878   for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
00879     WriteLayerData (FP, i, &(PASTEBUFFER->Data->Layer[i]));
00880   return (STATUS_OK);
00881 }
00882 
00886 static int
00887 WritePCB (FILE * FP)
00888 {
00889   Cardinal i;
00890   if (Settings.SaveMetricOnly)
00891     set_allow_readable (ALLOW_MM);
00892   else
00893     set_allow_readable (ALLOW_READABLE);
00894 
00895   WritePCBInfoHeader (FP);
00896   WritePCBDataHeader (FP);
00897   WritePCBFontData (FP);
00898   WriteAttributeList (FP, &PCB->Attributes, "");
00899   WriteViaData (FP, PCB->Data);
00900   WriteElementData (FP, PCB->Data);
00901   WritePCBRatData (FP);
00902   for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
00903     WriteLayerData (FP, i, &(PCB->Data->Layer[i]));
00904   WritePCBNetlistData (FP);
00905 
00906   return (STATUS_OK);
00907 }
00908 
00912 static int
00913 WritePCBFile (char *Filename)
00914 {
00915   FILE *fp;
00916   int result;
00917 
00918   if ((fp = fopen (Filename, "w")) == NULL)
00919     {
00920       OpenErrorMessage (Filename);
00921       return (STATUS_ERROR);
00922     }
00923   result = WritePCB (fp);
00924   fclose (fp);
00925   return (result);
00926 }
00927 
00932 static int
00933 WritePipe (char *Filename, bool thePcb)
00934 {
00935   FILE *fp;
00936   int result;
00937   char *p;
00938   static DynamicStringType command;
00939   int used_popen = 0;
00940 
00941   if (EMPTY_STRING_P (Settings.SaveCommand))
00942     {
00943       fp = fopen (Filename, "w");
00944       if (fp == 0)
00945         {
00946           Message ("Unable to write to file %s\n", Filename);
00947           return STATUS_ERROR;
00948         }
00949     }
00950   else
00951     {
00952       used_popen = 1;
00953       /* setup commandline */
00954       DSClearString (&command);
00955       for (p = Settings.SaveCommand; *p; p++)
00956         {
00957           /* copy character if not special or add string to command */
00958           if (!(*p == '%' && *(p + 1) == 'f'))
00959             DSAddCharacter (&command, *p);
00960           else
00961             {
00962               DSAddString (&command, Filename);
00963 
00964               /* skip the character */
00965               p++;
00966             }
00967         }
00968       DSAddCharacter (&command, '\0');
00969       printf ("write to pipe \"%s\"\n", command.Data);
00970       if ((fp = popen (command.Data, "w")) == NULL)
00971         {
00972           PopenErrorMessage (command.Data);
00973           return (STATUS_ERROR);
00974         }
00975     }
00976   if (thePcb)
00977     {
00978       if (PCB->is_footprint)
00979         {
00980           WriteElementData (fp, PCB->Data);
00981           result = 0;
00982         }
00983       else
00984         result = WritePCB (fp);
00985     }
00986   else
00987     result = WriteBuffer (fp);
00988 
00989   if (used_popen)
00990     return (pclose (fp) ? STATUS_ERROR : result);
00991   return (fclose (fp) ? STATUS_ERROR : result);
00992 }
00993 
01000 void
01001 SaveInTMP (void)
01002 {
01003   char filename[80];
01004 
01005   /* memory might have been released before this function is called */
01006   if (PCB && PCB->Changed)
01007     {
01008       sprintf (filename, EMERGENCY_NAME, (int) getpid ());
01009       Message (_("Trying to save your layout in '%s'\n"), filename);
01010       WritePCBFile (filename);
01011     }
01012 }
01013 
01019 static bool dont_save_any_more = false;
01020 void
01021 EmergencySave (void)
01022 {
01023 
01024   if (!dont_save_any_more)
01025     {
01026       SaveInTMP ();
01027       dont_save_any_more = true;
01028     }
01029 }
01030 
01031  void 
01032 DisableEmergencySave (void)
01033 {
01034   dont_save_any_more = true;
01035 }
01036 
01037 static hidval backup_timer;
01038 
01047 static void
01048 backup_cb (hidval data)
01049 {
01050   backup_timer.ptr = NULL;
01051   Backup ();
01052   if (Settings.BackupInterval > 0 && gui->add_timer)
01053     backup_timer = gui->add_timer (backup_cb, 
01054                                    1000 * Settings.BackupInterval, data);
01055 }
01056 
01057 void
01058 EnableAutosave (void)
01059 {
01060   hidval x;
01061 
01062   x.ptr = NULL;
01063 
01064   /* If we already have a timer going, then cancel it out */
01065   if (backup_timer.ptr != NULL && gui->stop_timer)
01066     gui->stop_timer (backup_timer);
01067 
01068   backup_timer.ptr = NULL;
01069   /* Start up a new timer */
01070   if (Settings.BackupInterval > 0 && gui->add_timer)
01071     backup_timer = gui->add_timer (backup_cb, 
01072                                    1000 * Settings.BackupInterval, 
01073                                    x);
01074 }
01075 
01083 void
01084 Backup (void)
01085 {
01086   char *filename = NULL;
01087 
01088   if( PCB && PCB->Filename )
01089     {
01090       filename  = (char *) malloc (sizeof (char) * (strlen (PCB->Filename) + 2));
01091       if (filename == NULL)
01092         {
01093           fprintf (stderr, "Backup():  malloc failed\n");
01094           exit (1);
01095         }
01096       sprintf (filename, "%s~", PCB->Filename);
01097     }
01098   else
01099     {
01100       /* BACKUP_NAME has %.8i which  will be replaced by the process ID */
01101       filename  = (char *) malloc (sizeof (char) * (strlen (BACKUP_NAME) + 8));
01102       if (filename == NULL)
01103         {
01104           fprintf (stderr, "Backup():  malloc failed\n");
01105           exit (1);
01106         }
01107       sprintf (filename, BACKUP_NAME, (int) getpid ());
01108     }
01109 
01110   WritePCBFile (filename);
01111   free (filename);
01112 }
01113 
01114 #if !defined(HAS_ATEXIT) && !defined(HAS_ON_EXIT)
01115 
01123 void
01124 SaveTMPData (void)
01125 {
01126   sprintf (TMPFilename, EMERGENCY_NAME, (int) getpid ());
01127   WritePCBFile (TMPFilename);
01128 }
01129 
01133 void
01134 RemoveTMPData (void)
01135 {
01136   unlink (TMPFilename);
01137 }
01138 #endif
01139 
01148 static int
01149 LoadNewlibFootprintsFromDir(char *libpath, char *toppath, bool recursive)
01150 {
01151   char olddir[MAXPATHLEN + 1];    /* The directory we start out in (cwd) */
01152   char subdir[MAXPATHLEN + 1];    /* The directory holding footprints to load */
01153   DIR *subdirobj;                 /* Interable object holding all subdir entries */
01154   struct dirent *subdirentry;     /* Individual subdir entry */
01155   struct stat buffer;             /* Buffer used in stat */
01156   LibraryMenuType *menu = NULL; /* Pointer to PCB's library menu structure */
01157   LibraryEntryType *entry;      /* Pointer to individual menu entry */
01158   size_t l;
01159   size_t len;
01160   int n_footprints = 0;           /* Running count of footprints found in this subdir */
01161 
01162   /* Cache old dir, then cd into subdir because stat is given relative file names. */
01163   memset (subdir, 0, sizeof subdir);
01164   memset (olddir, 0, sizeof olddir);
01165   if (GetWorkingDirectory (olddir) == NULL)
01166     {
01167       Message (_("LoadNewlibFootprintsFromDir: Could not determine initial working directory\n"));
01168       return 0;
01169     }
01170 
01171   if (strcmp (libpath, "(local)") == 0)
01172     strcpy (subdir, ".");
01173   else
01174     strcpy (subdir, libpath);
01175 
01176   if (chdir (subdir))
01177     {
01178       ChdirErrorMessage (subdir);
01179       return 0;
01180     }
01181 
01182   /* Determine subdir is abs path */
01183   if (GetWorkingDirectory (subdir) == NULL)
01184     {
01185       Message (_("LoadNewlibFootprintsFromDir: Could not determine new working directory\n"));
01186       if (chdir (olddir))
01187         ChdirErrorMessage (olddir);
01188       return 0;
01189     }
01190 
01191   /* First try opening the directory specified by path */
01192   if ( (subdirobj = opendir (subdir)) == NULL )
01193     {
01194       OpendirErrorMessage (subdir);
01195       if (chdir (olddir))
01196         ChdirErrorMessage (olddir);
01197       return 0;
01198     }
01199 
01200   /* Get pointer to memory holding menu */
01201   menu = GetLibraryMenuMemory (&Library);
01202   /* Populate menuname and path vars */
01203   menu->Name = strdup (subdir);
01204   menu->directory = strdup (toppath);
01205 
01206   /* Now loop over files in this directory looking for files.
01207    * We ignore certain files which are not footprints.
01208    */
01209   while ((subdirentry = readdir (subdirobj)) != NULL)
01210   {
01211 #ifdef DEBUG
01212 /*    printf("...  Examining file %s ... \n", subdirentry->d_name); */
01213 #endif
01214 
01215     /* Ignore non-footprint files found in this directory
01216      * We're skipping .png and .html because those
01217      * may exist in a library tree to provide an html browsable
01218      * index of the library.
01219      */
01220     l = strlen (subdirentry->d_name);
01221     if (!stat (subdirentry->d_name, &buffer) && S_ISREG (buffer.st_mode)
01222       && subdirentry->d_name[0] != '.'
01223       && NSTRCMP (subdirentry->d_name, "CVS") != 0
01224       && NSTRCMP (subdirentry->d_name, "Makefile") != 0
01225       && NSTRCMP (subdirentry->d_name, "Makefile.am") != 0
01226       && NSTRCMP (subdirentry->d_name, "Makefile.in") != 0
01227       && (l < 4 || NSTRCMP(subdirentry->d_name + (l - 4), ".png") != 0) 
01228       && (l < 5 || NSTRCMP(subdirentry->d_name + (l - 5), ".html") != 0)
01229       && (l < 4 || NSTRCMP(subdirentry->d_name + (l - 4), ".pcb") != 0) )
01230       {
01231 #ifdef DEBUG
01232 /*      printf("...  Found a footprint %s ... \n", subdirentry->d_name); */
01233 #endif
01234         n_footprints++;
01235         entry = GetLibraryEntryMemory (menu);
01236 
01237         /* 
01238          * entry->AllocatedMemory points to abs path to the footprint.
01239          * entry->ListEntry points to fp name itself.
01240          */
01241         len = strlen(subdir) + strlen("/") + strlen(subdirentry->d_name) + 1;
01242         entry->AllocatedMemory = (char *)calloc (1, len);
01243         strcat (entry->AllocatedMemory, subdir);
01244         strcat (entry->AllocatedMemory, PCB_DIR_SEPARATOR_S);
01245 
01246         /* store pointer to start of footprint name */
01247         entry->ListEntry = entry->AllocatedMemory
01248             + strlen (entry->AllocatedMemory);
01249 
01250         /* Now place footprint name into AllocatedMemory */
01251         strcat (entry->AllocatedMemory, subdirentry->d_name);
01252 
01253         /* mark as directory tree (newlib) library */
01254         entry->Template = (char *) -1;
01255       }
01256   }
01257   closedir (subdirobj);
01258 
01259   /* Don't recurse into relatively-specified directories--we might be
01260      in the user's working directory, and the path might be "." */
01261   if (!recursive) {
01262     if (chdir (olddir))
01263       ChdirErrorMessage (olddir);
01264     return n_footprints;
01265   }
01266 
01267   /* Then open this dir so we can loop over its contents. */
01268   if ((subdirobj = opendir (subdir)) == NULL)
01269     {
01270       OpendirErrorMessage (subdir);
01271       if (chdir (olddir))
01272         ChdirErrorMessage (olddir);
01273       return 0;
01274     }
01275 
01276   /* Now loop over files in this directory looking for subdirs.
01277    * For each direntry which is a valid subdirectory,
01278    * try to load newlib footprints inside it.
01279    */
01280   while ((subdirentry = readdir (subdirobj)) != NULL)
01281     {
01282 #ifdef DEBUG
01283       printf("In ParseLibraryTree loop examining 2nd level direntry %s ... \n", subdirentry->d_name);
01284 #endif
01285       /* Find subdirectories.  Ignore entries beginning with "." and CVS
01286        * directories.
01287        */
01288       if (!stat (subdirentry->d_name, &buffer)
01289           && S_ISDIR (buffer.st_mode)
01290           && subdirentry->d_name[0] != '.'
01291           && NSTRCMP (subdirentry->d_name, "CVS") != 0)
01292         {
01293           /* Found a valid subdirectory.  Try to load footprints from it.
01294            */
01295           char *subdir_path = (char *)calloc (
01296             1, strlen(subdir) + strlen("/") + strlen(subdirentry->d_name) + 1);
01297           if (subdir_path == NULL)
01298             {
01299               fprintf (stderr, "LoadNewlibFootprintsFromDir():  "
01300                                "malloc failed\n");
01301               closedir (subdirobj);
01302               if (chdir (olddir))
01303                 ChdirErrorMessage (olddir);
01304               return n_footprints;
01305             }
01306           strcat (subdir_path, subdir);
01307           strcat (subdir_path, PCB_DIR_SEPARATOR_S);
01308           strcat (subdir_path, subdirentry->d_name);
01309 
01310           n_footprints += LoadNewlibFootprintsFromDir(subdir_path, toppath, true);
01311           free(subdir_path);
01312         }
01313     }
01314   /* Done.  Clean up, cd back into old dir, and return */
01315   closedir (subdirobj);
01316   if (chdir (olddir))
01317     ChdirErrorMessage (olddir);
01318   return n_footprints;
01319 }
01320 
01321 
01329 static int
01330 ParseLibraryTree (void)
01331 {
01332   char toppath[MAXPATHLEN + 1];    /* String holding abs path to top level library dir */
01333   char working[MAXPATHLEN + 1];    /* String holding abs path to working dir */
01334   char *libpaths;                  /* String holding list of library paths to search */
01335   char *p;                         /* Helper string used in iteration */
01336   int n_footprints = 0;            /* Running count of footprints found */
01337   bool is_abs = false;             /* If we are processing an absolute path */
01338 
01339   /* Initialize path, working by writing 0 into every byte. */
01340   memset (toppath, 0, sizeof toppath);
01341   memset (working, 0, sizeof working);
01342 
01343   /* Save the current working directory as an absolute path.
01344    * This fcn writes the abs path into the memory pointed to by the input arg.
01345    */
01346   if (GetWorkingDirectory (working) == NULL)
01347     {
01348       Message (_("ParseLibraryTree: Could not determine initial working directory\n"));
01349       return 0;
01350     }
01351 
01352   /* Additional loop to allow for multiple 'newlib' style library directories 
01353    * called out in Settings.LibraryTree
01354    */
01355   libpaths = strdup (Settings.LibraryTree);
01356   for (p = strtok (libpaths, PCB_PATH_DELIMETER); p && *p; p = strtok (NULL, PCB_PATH_DELIMETER))
01357     {
01358       /* remove trailing path delimeter */
01359       strncpy (toppath, p, sizeof (toppath) - 1);
01360 
01361       /* start out in the working directory in case the path is a
01362        * relative path 
01363        */
01364       if (chdir (working))
01365         {
01366           ChdirErrorMessage (working);
01367           free (libpaths);
01368           return 0;
01369         }
01370 
01371       /*
01372        * Next change to the directory which is the top of the library tree
01373        * and extract its abs path.
01374        */
01375       if (chdir (toppath))
01376         {
01377           ChdirErrorMessage (toppath);
01378           continue;
01379         }
01380 
01381       if (GetWorkingDirectory (toppath) == NULL)
01382         {
01383           Message (_("ParseLibraryTree: Could not determine new working directory\n"));
01384           continue;
01385         }
01386 
01387       /* figure out if this is an absolute path.  Make sure it works on win32 as well. */
01388       if (*p == PCB_DIR_SEPARATOR_C)
01389         {
01390          is_abs = true;
01391         }
01392       else if (strlen(p) > 3 && isalpha ((int) p[0]) && p[1] == ':' && p[2] == PCB_DIR_SEPARATOR_C)
01393         {
01394          is_abs = true;
01395         }
01396 
01397 #ifdef DEBUG
01398       printf("In ParseLibraryTree, looking for newlib footprints inside top level directory %s ... \n", 
01399              toppath);
01400 #endif
01401 
01402       /* Next read in any footprints in the top level dir and below */
01403       n_footprints += LoadNewlibFootprintsFromDir("(local)", toppath, is_abs);
01404     }
01405 
01406   /* restore the original working directory */
01407   if (chdir (working))
01408     ChdirErrorMessage (working);
01409 
01410 #ifdef DEBUG
01411   printf("Leaving ParseLibraryTree, found %d footprints.\n", n_footprints);
01412 #endif
01413 
01414   free (libpaths);
01415   return n_footprints;
01416 }
01417 
01424 int
01425 ReadLibraryContents (void)
01426 {
01427   static char *command = NULL;
01428   char inputline[MAX_LIBRARY_LINE_LENGTH + 1];
01429   FILE *resultFP = NULL;
01430   LibraryMenuType *menu = NULL;
01431   LibraryEntryType *entry;
01432 
01433   /* If we don't have a command to execute to find the library contents,
01434    * skip this. This is used by default on Windows builds (set in main.c),
01435    * as we can't normally run shell scripts or expect to have m4 present.
01436    */
01437   if (Settings.LibraryContentsCommand != NULL &&
01438       Settings.LibraryContentsCommand[0] != '\0')
01439     {
01440       /*  First load the M4 stuff.  The variable Settings.LibraryPath
01441        *  points to it.
01442        */
01443       free (command);
01444       command = EvaluateFilename (Settings.LibraryContentsCommand,
01445                                   Settings.LibraryPath, Settings.LibraryFilename,
01446                                   NULL);
01447 
01448 #ifdef DEBUG
01449       printf("In ReadLibraryContents, about to execute command %s\n", command);
01450 #endif
01451 
01452       /* This uses a pipe to execute a shell script which provides the names of
01453        * all M4 libs and footprints.  The results are placed in resultFP.
01454        */
01455       if (command && *command && (resultFP = popen (command, "r")) == NULL)
01456         {
01457           PopenErrorMessage (command);
01458         }
01459 
01460       /* the M4 library contents are separated by colons;
01461        * template : package : name : description
01462        */
01463       while (resultFP != NULL && fgets (inputline, MAX_LIBRARY_LINE_LENGTH, resultFP))
01464         {
01465           size_t len = strlen (inputline);
01466 
01467           /* check for maximum linelength */
01468           if (len)
01469             {
01470               len--;
01471               if (inputline[len] != '\n')
01472                 Message
01473                   ("linelength (%i) exceeded; following characters will be ignored\n",
01474                    MAX_LIBRARY_LINE_LENGTH);
01475               else
01476                 inputline[len] = '\0';
01477             }
01478 
01479           /* if the line defines a menu */
01480           if (!strncmp (inputline, "TYPE=", 5))
01481             {
01482               menu = GetLibraryMenuMemory (&Library);
01483               menu->Name = strdup (UNKNOWN (&inputline[5]));
01484               menu->directory = strdup (Settings.LibraryFilename);
01485             }
01486           else
01487             {
01488               /* allocate a new menu entry if not already done */
01489               if (!menu)
01490                 {
01491                   menu = GetLibraryMenuMemory (&Library);
01492                   menu->Name = strdup (UNKNOWN ((char *) NULL));
01493                   menu->directory = strdup (Settings.LibraryFilename);
01494                 }
01495               entry = GetLibraryEntryMemory (menu);
01496               entry->AllocatedMemory = strdup (inputline);
01497 
01498               /* now break the line into pieces separated by colons */
01499               if ((entry->Template = strtok (entry->AllocatedMemory, ":")) !=
01500                   NULL)
01501                 if ((entry->Package = strtok (NULL, ":")) != NULL)
01502                   if ((entry->Value = strtok (NULL, ":")) != NULL)
01503                     entry->Description = strtok (NULL, ":");
01504 
01505               /* create the list entry */
01506               len = strlen (EMPTY (entry->Value)) +
01507                 strlen (EMPTY (entry->Description)) + 4;
01508               entry->ListEntry = (char *)calloc (len, sizeof (char));
01509               sprintf (entry->ListEntry,
01510                        "%s, %s", EMPTY (entry->Value),
01511                        EMPTY (entry->Description));
01512             }
01513         }
01514       if (resultFP != NULL)
01515         pclose (resultFP);
01516     }
01517 
01518   /* Now after reading in the M4 libs, call a function to
01519    * read the newlib footprint libraries.  Then sort the whole
01520    * library.
01521    */
01522   if (ParseLibraryTree () > 0 || resultFP != NULL)
01523     {
01524       sort_library (&Library);
01525       return 0;
01526     }
01527   
01528   return (1);
01529 }
01530 
01531 #define BLANK(x) ((x) == ' ' || (x) == '\t' || (x) == '\n' \
01532                 || (x) == '\0')
01533 
01537 int
01538 ReadNetlist (char *filename)
01539 {
01540   static char *command = NULL;
01541   char inputline[MAX_NETLIST_LINE_LENGTH + 1];
01542   char temp[MAX_NETLIST_LINE_LENGTH + 1];
01543   FILE *fp;
01544   LibraryMenuType *menu = NULL;
01545   LibraryEntryType *entry;
01546   int i, j, lines, kind;
01547   bool continued;
01548   bool used_popen = false;
01549   int retval = 0;
01550 
01551   if (!filename)
01552     return 1;                   /* nothing to do */
01553 
01554   Message (_("Importing PCB netlist %s\n"), filename);
01555 
01556   if (EMPTY_STRING_P (Settings.RatCommand))
01557     {
01558       fp = fopen (filename, "r");
01559       if (!fp)
01560         {
01561           Message("Cannot open %s for reading", filename);
01562           return 1;
01563         }
01564     }
01565   else
01566     {
01567       used_popen = true;
01568       free (command);
01569       command = EvaluateFilename (Settings.RatCommand,
01570                                   Settings.RatPath, filename, NULL);
01571 
01572       /* open pipe to stdout of command */
01573       if (*command == '\0' || (fp = popen (command, "r")) == NULL)
01574         {
01575           PopenErrorMessage (command);
01576           return 1;
01577         }
01578     }
01579   lines = 0;
01580   /* kind = 0  is net name
01581    * kind = 1  is route style name
01582    * kind = 2  is connection
01583    */
01584   kind = 0;
01585   while (fgets (inputline, MAX_NETLIST_LINE_LENGTH, fp))
01586     {
01587       size_t len = strlen (inputline);
01588       /* check for maximum length line */
01589       if (len)
01590         {
01591           if (inputline[--len] != '\n')
01592             Message (_("Line length (%i) exceeded in netlist file.\n"
01593                        "additional characters will be ignored.\n"),
01594                      MAX_NETLIST_LINE_LENGTH);
01595           else
01596             inputline[len] = '\0';
01597         }
01598       continued = (inputline[len - 1] == '\\') ? true : false;
01599       if (continued)
01600         inputline[len - 1] = '\0';
01601       lines++;
01602       i = 0;
01603       while (inputline[i] != '\0')
01604         {
01605           j = 0;
01606           /* skip leading blanks */
01607           while (inputline[i] != '\0' && BLANK (inputline[i]))
01608             i++;
01609           if (kind == 0)
01610             {
01611               /* add two spaces for included/unincluded */
01612               temp[j++] = ' ';
01613               temp[j++] = ' ';
01614             }
01615           while (!BLANK (inputline[i]))
01616             temp[j++] = inputline[i++];
01617           temp[j] = '\0';
01618           while (inputline[i] != '\0' && BLANK (inputline[i]))
01619             i++;
01620           if (kind == 0)
01621             {
01622               menu = GetLibraryMenuMemory (&PCB->NetlistLib);
01623               menu->Name = strdup (temp);
01624               menu->flag = 1;
01625               kind++;
01626             }
01627           else
01628             {
01629               if (kind == 1 && strchr (temp, '-') == NULL)
01630                 {
01631                   kind++;
01632                   menu->Style = strdup (temp);
01633                 }
01634               else
01635                 {
01636                   entry = GetLibraryEntryMemory (menu);
01637                   entry->ListEntry = strdup (temp);
01638                 }
01639             }
01640         }
01641       if (!continued)
01642         kind = 0;
01643     }
01644   if (!lines)
01645     {
01646       Message (_("Empty netlist file!\n"));
01647       retval = 1;
01648     }
01649   if (used_popen)
01650     pclose (fp);
01651   else
01652     fclose (fp);
01653   sort_netlist ();
01654   return retval;
01655 }
01656 
01657 static int ReadEdifNetlist (char *filename);
01658 
01659 int ImportNetlist (char *filename)
01660 {
01661   FILE *fp;
01662   char buf[16];
01663   int i;
01664   char* p;
01665   
01666 
01667   if (!filename) return (1);                    /* nothing to do */
01668   fp = fopen (filename, "r");
01669   if (!fp) return (1);                  /* bad filename */
01670   i = fread (buf, 1, sizeof(buf)-1, fp);
01671   fclose(fp);
01672   buf[i] = '\0';
01673   p=buf;
01674   while ( *p )
01675   {
01676       *p = tolower ((int) *p);
01677       p++;
01678   }
01679   p = strstr (buf, "edif");
01680   if (!p) return ReadNetlist (filename);
01681   else return ReadEdifNetlist (filename);
01682 }
01683 
01684 static int ReadEdifNetlist (char *filename)
01685 {
01686     Message (_("Importing edif netlist %s\n"), filename);
01687     ParseEDIF(filename, NULL);
01688     
01689     return 0;
01690 }
01691