#usage "en:<b>Export of libraries from a drawing.</b><p>\n"
          "This can be used to make individual changes to components in a project. "
          "Just load schematic, board or both, use this ULP to generate one or several libraries "
          "containing the used components. Make your changes in the library/libraries and use the "
          "UPDATE command to introduce the changes to schematic and/or board.<p>"
          "See Help for further details.<p>",
       "de:<b>Export von Bibliotheken aus einer Zeichnungsdatei.</b><p>\n"
          "Damit können Sie individuelle Änderungen an Komponenten in einem Projekt machen. "
          "Laden Sie dazu Schaltplan, Board oder beides, verwenden Sie dieses ULP, um eine oder mehrere Bibliotheken "
          "mit den verwendeten Komponenten zu generieren. Machen Sie Ihre Änderungen in der/den Bibliothek(en) "
          "und verwenden Sie den UPDATE-Befehl, um die Änderungen in Schaltplan und/oder Board zu bringen.<p>"
          "Siehe Hilfe für weitere Details.<p>"

#require 6.0301

string Version = "6.0";

// Language:
//----------
// Please keep to alphabetic sorting for maintainability !
string Dictionary[] = {
  "en\v"
  "de\v",
  "Back\v"
  "Zurück\v",
  "Cancel\v"
  "Abbrechen\v",
  "Creation mode\v"
  "Erzeugungsmodus\v",
  "&Browse\v"
  "&Durchsuchen\v",
  "Export EAGLE libraries - Help\v"
  "Export von EAGLE-Bibliotheken - Hilfe\v",
  "Export EAGLE libraries from drawing\v"
  "Export von EAGLE-Bibliotheken aus der Zeichnung\v",
  "Export original libraries\v"
  "Original-Bibliotheken exportieren\v",
  "File \v"
  "Datei \v",
  "Help\v"
  "Hilfe\v",
  "Load\v"
  "Laden\v",
  "Merge to one library, \v"
  "In eine Bibliothek integrieren, \v",
  "original library names as prefix for symbol, package and device names\v"
  "Original-Bibliotheksnamen als Präfix für Symbol-, Package- und Devicenamen verwenden\v",
  "Path\v"
  "Pfad\v",
  "Please start from schematic or board editor!\v"
  "Bitte starten Sie vom Schaltplan- oder Board-Editor!\v",
  "Start\v"
  "Start\v",
  "unchanged symbol, package and device names\v"
  "Symbol-, Package- und Devicenamen unverändert\v"
};

string DlgLang = language();
if (DlgLang != "de") DlgLang = "en";
int LangIdx = strstr(Dictionary[0], DlgLang) / 3;

// Translate, based on dictionary
string TR(string s) {
  string t = lookup(Dictionary, s, LangIdx, '\v');
  return t ? t : s;
}
//-----------------------------------------------------------------------------

// Used in DrawText
string AlignName[];
AlignName[ALIGN_BOTTOM_LEFT]   = "BOTTOM LEFT";
AlignName[ALIGN_BOTTOM_CENTER] = "BOTTOM CENTER";
AlignName[ALIGN_BOTTOM_RIGHT]  = "BOTTOM RIGHT";
AlignName[ALIGN_CENTER_LEFT]   = "CENTER LEFT";
AlignName[ALIGN_CENTER]        = "CENTER CENTER";
AlignName[ALIGN_CENTER_RIGHT]  = "CENTER RIGHT";
AlignName[ALIGN_TOP_LEFT]      = "TOP LEFT";
AlignName[ALIGN_TOP_CENTER]    = "TOP CENTER";
AlignName[ALIGN_TOP_RIGHT]     = "TOP RIGHT";

string Croute[];
Croute[CONTACT_ROUTE_ALL] = "ALL";
Croute[CONTACT_ROUTE_ANY] = "ANY";  // 2012-09-26

// Used in DrawDimension
string DimType[];
DimType[DIMENSION_PARALLEL] = "PARALLEL";
DimType[DIMENSION_HORIZONTAL] = "HORIZONTAL";
DimType[DIMENSION_VERTICAL] = "VERTICAL";
DimType[DIMENSION_RADIUS] = "RADIUS";
DimType[DIMENSION_DIAMETER] = "DIAMETER";
DimType[DIMENSION_ANGLE] = "ANGLE";
DimType[DIMENSION_LEADER] = "LEADER";

string UnitName[];
UnitName[GRID_UNIT_MIC] = "MIC";
UnitName[GRID_UNIT_MM] = "MM";
UnitName[GRID_UNIT_MIL] = "MIL";
UnitName[GRID_UNIT_INCH] = "INCH";

string x[] = { " " };
string CurrentLbrName   = "";
string EditName;
string h, cmd = "";
string ScriptName, PureScriptName;
string WorkPath;
string Status = "";
int    n;
int    NameIndex = 0;
int    Merge = 0;

char   ascii127[]       =   { 181,  196,  214,  220,  223, 0 }; // not allowed character 2010-05-12
// www: In 8859-1 these chars are { � ,Ä ,Ö, Ü, ß } and therefore actually allowed as object names.
//      Change this if requested ! What's also allowed but deprecated is { ä, ö, ü }. Should be replaced
//      by { Ä, Ö, Ü }. All other chars allowed for file names are allwoed also for object names.
//      Lower case is automatically converted to upper case (except the umlauts).
string Newascii127[]    =   { "U", "AE", "OE", "UE", "SS", "" }; // change to allowed character

string HelpText[] = {
  "The library export collects the devices or packages from a schematic or board and stores them back "
  "into one or several libraries."
  "<p>"
  "By default all contained libraries are exported with their original name into the drawing path."
  "Thus you can edit symbols and/or packages in these libraries and "
  "use the UPDATE command to change all respective parts in schematic or board."
  "<p>"
  "Alternatively all objects can be merged into a single library with the name of the schematic or board."
  "You can choose between using the names of the original libraries as prefix for package, symbol and device names or not."
  "Not using these prefixes can lead to ambiguities and therefore is not recommended."
  "<p>"
  "The export path can be adjusted by 'Browse' to select a directory."
  "If one of the library files to be created already exists, you will be prompted if "
  "the existing file may be deleted."
  ,
  "Der Bibliotheks-Export sammelt Devices und Packages eines Schaltplans oder Boards und speichert "
  "sie in eine oder mehrere Bibliotheken."
  "<p>"
  "Standardmässig werden alle enthaltenen Bibliotheken mit ihrem Orignialnamen in den Pfad der Zeichnungsdatei(en) "
  "exportiert. So können Sie Symbole und/oder Packages editieren und mit dem UPDATE-Befehl alle entsprechenden "
  "Bauteile in Schaltplan und Board aktualisieren."
  "<p>"
  "Alternativ können alle Objekte in eine einzige Bibliothek mit Schaltplan- bzw. Boardnamen integriert werden. "
  "Sie können wählen, ob Sie die Namen der Original-Bibliotheken als Präfix für die Package-, Symbol- und Devicenamen verwenden "
  "oder nicht. Diese Präfixe nicht zu verwenden kann zu Mehrdeutigkeit führen und wird daher nicht empfohlen."
  "<p>"
  "Der Export-Pfad kann mit 'Durchsuchen' eingestellt werden. "
  "Wenn eines der Bibliotheken bereits existiert, werden Sie gefragt, ob die bestehende Datei gelöscht werden darf."
};

void DisplayHelp() {
  dlgDialog(TR("Export EAGLE libraries - Help")) {
    dlgHBoxLayout dlgSpacing(400);
    dlgTextView(HelpText[LangIdx]);
    dlgHBoxLayout {
      dlgStretch(1);
      dlgPushButton("+" + TR("Back")) dlgReject();
      dlgStretch(1);
    }
  };
}

string get_project_path() {
  if (board)     board(B)     return(filedir(B.name));
  if (schematic) schematic(B) return(filedir(B.name));
  if (library)   library(B)   return(filedir(B.name));
}

string replacenewline(string nl) {
  string a[];
  int n = strsplit(a, nl, '\n');
  if (n > 0) {
    nl = "";
    for (int x = 0; x < n - 1; x++) {
      nl += a[x] + "\\n";
    }
    nl += a[x];
  }
  return nl;
}

// Da der ganze String in ' eingeschlossen wird,
// müssen die Apostrophen verdoppelt werden.
string addApostroph(string name) {  // 2012-03-23
  string t[];
  int cnt = strsplit(t, name, '\''); // check Apostroph
  if (cnt > 1) {
    name = "";
    for (int i = 0; i < cnt; i++) {
      if (i == 0) {
        if (t[i]) name += t[i];
      }
      else if (i) name += "''" + t[i];
    }
  }
  return name;
}

string replaceascii127(string s, int n, string newascii) {
  if (n == strlen(s)) return strsub(s, 0, n) + newascii;
  else return strsub(s, 0, n) + newascii + strsub(s, n+1);
}

string check_ascii127(string s) {  // 2009-02-02
  int n = 0;
  int pos;
  do {
    pos = strchr(s, ascii127[n]);
    if (pos > -1)
      s = replaceascii127(s, pos, Newascii127[n]);
    if (pos < 0) n++;  // 2012-02-08
  } while (ascii127[n]);
  return s;
}

// Remove this as soon as the "@-problem" is solved !
string ReplaceAt(string s) {
  for (int i = 0; i < strlen(s); i++)
      if (s[i]=='@') s[i]='$';  // Creates problems when name is used in ADD and other commands
  return s;
}

string ReplaceInvalidCharsForObjNames(string s) {
  for (int i = 0; i < strlen(s); i++)
      if (s[i]==' ') s[i]='-';  // No '_' to distinguish from prefix ending !

  return ReplaceAt(s);
}

string LbrPrefix(string s) {
  s = check_ascii127(s);
  return ReplaceInvalidCharsForObjNames(s) + "_";
}

int exist_file(string FileName) {
  string a[];
  int n = fileglob(a, FileName);
  if (n == 0) return 0;
  else return 1;
}

void CreateLBRdescription(string project_name) {
  // !!! Wird nicht vorherige Description überschrieben ?
  sprintf(h, "DESCRIPTION 'Generated from <b>%s</b><p>\\n\\\nby %s';\n", filename(project_name), filename(argv[0]));
  cmd += h;
  return;
}

void CreateHeader(UL_LIBRARY LBR) {
  LBR.layers(L) {
    sprintf(h, "Layer %d %s;\n", L.number, L.name); cmd += h;
  }
  sprintf(h, "Set Wire_bend 2;\nSet Select_factor 0;\nSet Undo_log off;\n"); cmd += h;
  sprintf(h, "Grid mic 1;\n"); cmd += h;
  sprintf(h, "Display All;\n"); cmd += h;
  cmd  += "DESCRIPTION '" + addApostroph(replacenewline(LBR.description)) + "';\n";
}

void CreateTrailer(void) {
  sprintf(h, "Set Undo_log On;\nSet Select_factor 0.02;\nGrid last;\n"); cmd += h;
}

void PrintValidLayer(int LNr) {
  sprintf(h, "Layer %d;\n", LNr); cmd += h;
}

void DrawDimension(UL_DIMENSION D) { // 2011-12-06
  sprintf(h, "CHANGE LAYER %d;\n", D.layer); cmd += h;
  sprintf(h, "CHANGE SIZE %f;\n", u2mic(D.size)); cmd += h;
  sprintf(h, "CHANGE RATIO %d;\n", D.ratio); cmd += h;
  sprintf(h, "CHANGE DUNIT %s %s %d;\n", UnitName[D.unit], (D.visible==0) ? "off" : "on", D.precision); cmd += h;
  sprintf(h, "CHANGE DLINE %f %f %f %f;\n", u2mic(D.width), u2mic(D.extwidth), u2mic(D.extlength), u2mic(D.extoffset)); cmd += h;
  sprintf(h, "DIMENSION %s (C%f %f) (%f %f) (%f %f);\n",  // Warum das C ?
              DimType[D.dtype],
              u2mic(D.x1), u2mic(D.y1),
              u2mic(D.x2), u2mic(D.y2),
              u2mic(D.x3), u2mic(D.y3) );
  cmd += h;
  return;
}

void DrawFrame(UL_FRAME F) {
  PrintValidLayer(F.layer);

  string FBorder = "";
  if (F.border & 1) FBorder += " BOTTOM";
  if (F.border & 2) FBorder += " RIGHT";
  if (F.border & 4) FBorder += " TOP";
  if (F.border & 8) FBorder += " LEFT";

  sprintf(h, "FRAME %d %d %s (%f %f) (%f %f);\n",
            F.columns, F.rows,
            FBorder,
            u2mic(F.x1), u2mic(F.y1),
            u2mic(F.x2), u2mic(F.y2) );
            cmd += h;
  return;
}

void DrawCircle(UL_CIRCLE C) {
  PrintValidLayer(C.layer);
  sprintf(h, "Circle %f (%f %f) (%f %f);\n",
            u2mic(C.width),
            u2mic(C.x), u2mic(C.y),
            u2mic(C.x + C.radius), u2mic(C.y));
            cmd += h;
  return;
}

void DrawWire(UL_WIRE W) {
  PrintValidLayer(W.layer);
  if (W.arc) {    // 2008-09-11
    sprintf(h, "WIRE %f %s (%f %f) %+f (%.f %.f);\n",
              u2mic(W.width),
              W.cap == CAP_ROUND ? "ROUND" : "FLAT",  // 2011-09-07
              u2mic(W.x1), u2mic(W.y1),
              W.curve,
              u2mic(W.x2), u2mic(W.y2));
    cmd += h;
  }
  else {
    sprintf(h, "Wire %f (%f %f) (%f %f);\n",
            u2mic(W.width), u2mic(W.x1), u2mic(W.y1), u2mic(W.x2), u2mic(W.y2));
    cmd += h;
  }
}

void DrawRectangle(UL_RECTANGLE R) {
  PrintValidLayer(R.layer);
  sprintf(h, "Rect R%.1f (%f %f) (%f %f);\n",
           R.angle,
           u2mic(R.x1), u2mic(R.y1),
           u2mic(R.x2), u2mic(R.y2));
           cmd += h;
}

void DrawContact(UL_CONTACT C) {
  string ShapeString;
  string ShapeFlag;
  if (C.pad) {
    switch(C.pad.shape[17]) {  //!!!
      case PAD_SHAPE_SQUARE    : ShapeString = "Square";  break;
      case PAD_SHAPE_ROUND     : ShapeString = "Round";   break;
      case PAD_SHAPE_OCTAGON   : ShapeString = "Octagon"; break;
      case PAD_SHAPE_LONG      : ShapeString = "Long";    break;
      case PAD_SHAPE_OFFSET    : ShapeString = "Offset";  break;
    }
    if (!(C.pad.flags & PAD_FLAG_STOP) )     ShapeFlag  = "NOSTOP ";
    if (!(C.pad.flags & PAD_FLAG_THERMALS) ) ShapeFlag += "NOTHERMALS ";
    if ((C.pad.flags & PAD_FLAG_FIRST) )     ShapeFlag += "FIRST ";     // 22.11.2004 support@cadsoft
    //
    // PAD [diameter] [shape] [orientation] [flags] ['name'] *..
    //
     sprintf(h, "Change Drill %f;\n", u2mic(C.pad.drill)); cmd += h;
     sprintf(h, "Pad %f %s R%.1f %s '%s' (%f %f);\n",
               u2mic(C.pad.diameter[17]),
               ShapeString,
               C.pad.angle,
               ShapeFlag,
               addApostroph(C.pad.name),  // 2012-03-29
               u2mic(C.pad.x), u2mic(C.pad.y));
               cmd += h;
  }
  else if (C.smd) {
    if (!(C.smd.flags & PAD_FLAG_STOP) )     ShapeFlag  = "NOSTOP ";
    if (!(C.smd.flags & SMD_FLAG_THERMALS) ) ShapeFlag += "NOTHERMALS ";
    if (!(C.smd.flags & SMD_FLAG_CREAM) )    ShapeFlag += "NOCREAM ";

    PrintValidLayer(C.smd.layer);
     sprintf(h, "CHANGE Roundness %d;\n", C.smd.roundness); cmd += h;
    //
    // SMD [x_width y_width] [-roundness] [orientation] [flags] ['name'] *..
    //
     sprintf(h, "SMD %f %f -%d R%.1f %s '%s' (%f %f);\n",
             u2mic(C.smd.dx), u2mic(C.smd.dy),
             C.smd.roundness,
             C.smd.angle,
             ShapeFlag,
             addApostroph(C.smd.name),  // 2012-03-29
             u2mic(C.smd.x), u2mic(C.smd.y));
             cmd += h;
  }
}

void DrawText(UL_TEXT T) {
  PrintValidLayer(T.layer);
  switch(T.font) {
       case FONT_VECTOR       : sprintf(h, "CHANGE FONT VECTOR;\n");
                                cmd += h;
                                break;
       case FONT_PROPORTIONAL : sprintf(h, "CHANGE FONT PROPORTIONAL;\n");
                                cmd += h;
                                break;
       case FONT_FIXED        : sprintf(h, "CHANGE FONT FIXED;\n");
                                cmd += h;
                                break;
  }

  string Spin = "";
  string Mirror = "";
  if (T.spin) Spin = "S";
  if (T.mirror) Mirror = "M";

  sprintf(h, "Change Size %f;\n", u2mic(T.size));
  cmd += h;
  sprintf(h, "Change Ratio %d;\n", T.ratio);
  cmd += h;
  sprintf(h, "Change Align %s;\n", AlignName[T.align]);
  cmd += h;
  sprintf(h, "Change Linedistance %d;\n", T.linedistance);
  cmd += h;

  sprintf(h, "Text %s%sR%.1f '%s' (%f %f);\n",
           Spin, Mirror, T.angle, addApostroph(T.value), u2mic(T.x), u2mic(T.y) // AddApostroph ?
         );
  cmd += h;
}

void DrawHole(UL_HOLE H) {
  sprintf(h, "Change Drill %f;\n", u2mic(H.drill)); cmd += h;
  sprintf(h, "Hole (%f %f);\n", u2mic(H.x), u2mic(H.y)); cmd += h;
}

void DrawPolygon(UL_POLYGON PL) {
  PrintValidLayer(PL.layer);
  //sprintf(h, "Change Isolate %f;\n", u2mic(PL.isolate)); cmd += h; exist only in board
  sprintf(h, "Change Spacing %f;\n", u2mic(PL.spacing)); cmd += h;
  if (PL.pour == POLYGON_POUR_SOLID) {
     sprintf(h, "Change Pour Solid;\n"); cmd += h;
  }
  else {
     sprintf(h, "Change Pour Hatch;\n"); cmd += h;
  }
  sprintf(h, "Polygon %f ", u2mic(PL.width)); cmd += h;
  PL.wires(W) {
    sprintf(h, "(%f %f) ", u2mic(W.x1), u2mic(W.y1)); cmd += h; /*start coord.*/
    break;
  }
  PL.wires(W) {
    sprintf(h, " %+f (%f %f) ", W.curve, u2mic(W.x2), u2mic(W.y2)); cmd += h;
  }
  sprintf(h, ";\n"); cmd += h;
}

void DrawPin(UL_PIN P) {
  string DIR = "", FUNC = "", LEN = "", VIS = "", ANGLE = "R0";
  if (P.angle == 90)  (ANGLE  = "R90");
  if (P.angle == 180) (ANGLE  = "R180");
  if (P.angle == 270) (ANGLE  = "R270");

  if (P.function == PIN_FUNCTION_FLAG_NONE)  (FUNC = "None");
  if (P.function == PIN_FUNCTION_FLAG_DOT)   (FUNC  = "Dot");
  if (P.function == PIN_FUNCTION_FLAG_CLK)   (FUNC  = "Clk");
  if (P.function == (PIN_FUNCTION_FLAG_DOT | PIN_FUNCTION_FLAG_CLK))
                                             (FUNC  = "DotClk");

  if (P.visible == PIN_VISIBLE_FLAG_OFF)    (VIS  = "Off");
  if (P.visible == PIN_VISIBLE_FLAG_PIN)    (VIS  = "Pin");
  if (P.visible == PIN_VISIBLE_FLAG_PAD)    (VIS  = "Pad");
  if (P.visible == (PIN_VISIBLE_FLAG_PIN | PIN_VISIBLE_FLAG_PAD))
                                            (VIS  = "Both");

  switch(P.direction) {
    case PIN_DIRECTION_NC  : DIR = "NC";  break;
    case PIN_DIRECTION_IN  : DIR = "In";  break;
    case PIN_DIRECTION_OUT : DIR = "Out"; break;
    case PIN_DIRECTION_IO  : DIR = "IO"; break;  // 2011-10-10
    case PIN_DIRECTION_OC  : DIR = "OC";  break;
    case PIN_DIRECTION_PWR : DIR = "Pwr"; break;
    case PIN_DIRECTION_PAS : DIR = "Pas"; break;
    case PIN_DIRECTION_HIZ : DIR = "Hiz"; break;
    case PIN_DIRECTION_SUP : DIR = "Sup";
  }

  switch(P.length) {
    case PIN_LENGTH_POINT  : LEN = "Point";  break;
    case PIN_LENGTH_SHORT  : LEN = "Short";  break;
    case PIN_LENGTH_MIDDLE : LEN = "Middle"; break;
    case PIN_LENGTH_LONG   : LEN = "Long";
  }

  sprintf(h, "Pin '%s' %s %s %s %s %s %d (%f %f);\n",
  addApostroph(P.name),  // 2012-02-08
  DIR, FUNC, LEN, ANGLE, VIS, P.swaplevel, u2mic(P.x), u2mic(P.y));
  cmd += h;
}

void DrawSymbol(UL_SYMBOL S) {
  S.circles(C)    DrawCircle(C);
  S.rectangles(R) DrawRectangle(R);
  S.wires(W)      DrawWire(W);
  S.pins(P)       DrawPin(P);
  S.texts(T)      DrawText(T);
  S.polygons(PL)  DrawPolygon(PL);
  S.frames(F)     DrawFrame(F);
  S.dimensions(D) DrawDimension(D);
  cmd  += "DESCRIPTION '" + addApostroph(replacenewline(S.description)) + "';\n";
}

void DrawPackage(UL_PACKAGE P) {
  sprintf(h, "GRID mic;\n"); cmd+=h; // 2011-04-11
  P.circles(C)    DrawCircle(C);
  P.wires(W)      DrawWire(W);
  P.rectangles(R) DrawRectangle(R);
  P.contacts(C)   DrawContact(C);
  P.texts(T)      DrawText(T);
  P.holes(H)      DrawHole(H);
  P.polygons(PL)  DrawPolygon(PL);
  P.frames(F)     DrawFrame(F);
  P.dimensions(D) DrawDimension(D);
  cmd  += "DESCRIPTION '" + addApostroph(replacenewline(P.description)) + "';\n";
}

//          ************************************************
// see also *** export-schematic_mil-board_mm-script.ulp ***
//          ************************************************

void DrawDevice(UL_DEVICESET D, UL_LIBRARY LBR) {
  cmd  += "DESCRIPTION '" + addApostroph(replacenewline(D.description)) + "';\n";
  cmd  += "PREFIX '" + D.prefix + "';\n";
  cmd  += "VALUE " + D.value + ";\n";
  D.gates(G) {
    string GateAddlevel;
    switch (G.addlevel) {
      case GATE_ADDLEVEL_NEXT    : GateAddlevel = "Next";    break;
      case GATE_ADDLEVEL_MUST    : GateAddlevel = "Must";    break;
      case GATE_ADDLEVEL_CAN     : GateAddlevel = "Can";     break;
      case GATE_ADDLEVEL_REQUEST : GateAddlevel = "Request"; break;
      case GATE_ADDLEVEL_ALWAYS  : GateAddlevel = "Always";
    };
    sprintf(h, "CHANGE Addlevel  %s;\n", GateAddlevel); cmd += h;
    sprintf(h, "CHANGE Swaplevel %d;\n", G.swaplevel); cmd += h;
    string symname = G.symbol.name; // No problem with @...
    if (Merge == 1)
      symname = LbrPrefix(LBR.name) + symname;
    sprintf(h, "ADD '%s' '%s' (%f %f);\n", addApostroph(symname), addApostroph(G.name), u2mic(G.x), u2mic(G.y)); // 2012-02-07
    cmd += h;
  }
  D.devices(DV) {
    if (DV.package) {
      string pacname = ReplaceAt(DV.package.name); // !!!
      if (Merge == 1)
        pacname = LbrPrefix(LBR.name) + pacname;
      cmd += "PACKAGE '" + addApostroph(pacname) + "' " + DV.name + ";\n"; // addApostroph here problematic !
    }
    DV.gates(G) {
      if (DV.package) {
        G.symbol.pins(P) {
          string cont = "";
          P.contacts(C) {  // 2011-12-06
            if (!cont) sprintf(h, "%s", addApostroph(C.name)); // 2012-03-29
            else sprintf(h, " %s", addApostroph(C.name)); // 2012-003-29
            cont += h;
          }
          if (cont) {
            sprintf(h, "CONNECT %s '%s.%s' '%s';\n",  // //2012-03-23 problem with # in pin name
                        Croute[P.route],
                        addApostroph(G.name), // 2012-03-29
                        addApostroph(P.name),                // 2012-03-29
                        cont
                   );
            cmd += h;
          }
        }
      }
    }
    string t[];
    int n = strsplit(t, DV.technologies, ' ');
    for (int i = 0; i < n; i++) {
      sprintf(h,"TECHNOLOGY '%s';\n", addApostroph(t[i]));  // 2012-03-23
      cmd += h;
      DV.attributes(A, t[i]) {
        string const = "";
        if (A.constant) const = " CONSTANT";
        sprintf(h,"ATTRIBUTE '%s' '%s' %s\n", A.name, addApostroph(A.value), const); // 2010-10-14
        cmd += h;
      }
    }
  }
  return;
}

//----------------
int is_new(void) { // n = nr of entries
  int i;
  if (n == 0) return 1;
  for (i = 0; i < n; i++) {
    if (x[n] == x[i]) {
       return(0);
    }
  }
  return 1;
}

// ---------
void CreateOneLibHeader(UL_LIBRARY LBR, string project_name) {
  if (exist_file(WorkPath+CurrentLbrName+".lbr")) {
     sprintf(h, "REMOVE '%s';\n", WorkPath+CurrentLbrName+".lbr"); cmd += h; // delete existing lbr
  }
  sprintf(h, "OPEN '~dummy~.lbr';\nCLOSE;\nOPEN '%s.lbr';\n", WorkPath+CurrentLbrName);  // 2012-02-07 to close an opened library first
  cmd += h;
  CreateHeader(LBR);
  CreateLBRdescription(project_name);
}

// ---------
void OutputPackages(UL_LIBRARY LBR) {
  LBR.packages(P) {
    string pacname = ReplaceAt(P.name);
    if (Merge == 1)
       pacname = LbrPrefix(LBR.name) + pacname;
    n++;
    x[n] = pacname;
    if (is_new()) {
      Status = "   PAC: " + P.name; dlgRedisplay();
      sprintf(h, "\nEdit '%s.PAC';\n", addApostroph(pacname)); cmd += h;
      DrawPackage(P);
    }
  }
}

void OutputSymbols(UL_LIBRARY LBR) {
  LBR.symbols(S) {
    string symname =  S.name;
    if (Merge == 1)
       symname = LbrPrefix(LBR.name) + symname;
    n++;
    x[n] = symname;
    if (is_new()) {
      Status = "   SYM: " + S.name;  dlgRedisplay();
      sprintf(h, "\nEdit '%s.SYM';\n", addApostroph(symname)); cmd += h;
      DrawSymbol(S);
    }
  }
}

void OutputDevices(UL_LIBRARY LBR) {
  LBR.devicesets(D) {
    string dname = D.name;
    if (Merge == 1)
       dname = LbrPrefix(LBR.name) + dname;
    n++;
    x[n] = dname;
    if (is_new()) {
      Status = "   DEV: " + D.name;  dlgRedisplay();
      NameIndex = 0;
      sprintf(h, "\nEdit '%s.DEV';\n", addApostroph(dname)); cmd += h;
      DrawDevice(D, LBR);
    }
  }
}

void CreateLbr(UL_LIBRARY LBR) {
  if (exist_file(WorkPath+CurrentLbrName+".lbr")) {
     sprintf(h, "REMOVE '%s.lbr';\n", WorkPath+CurrentLbrName);
     cmd += h; // delete existing lbr
  }
  sprintf(h, "OPEN '~dummy~.lbr';\nCLOSE;\nOPEN '%s.lbr';\n", WorkPath+CurrentLbrName);  // 2012-02-07 to close a opened library first
  cmd += h;
  CreateHeader(LBR);
}

void make_lbr(void) {
  if (board) board(B) {
    B.libraries(LBR) {
      CurrentLbrName = LBR.name;
      CreateLbr(LBR);
      LBR.packages(PAC) {
        OutputPackages(LBR);
        cmd += "WRITE;\n";
        break;
      };
    }
  }
  if (schematic) schematic(SCH) {
    SCH.libraries(LBR) {
      CurrentLbrName = LBR.name;
      CreateLbr(LBR);
      LBR.devices(DEV) {
        OutputPackages(LBR);
        n=0;
        OutputSymbols(LBR);
        n=0;
        OutputDevices(LBR);
        cmd += "WRITE;\n";
        break;
      };
    }
  }
  CreateTrailer();
}

//-------------------
void make_one_lbr(void) {
  int noLbrs = 1;
  if (board) board(B) {
    B.libraries(LBR) {
      CurrentLbrName = filesetext(EditName, "");
      CreateOneLibHeader(LBR, B.name);
      noLbrs = 0;
      break;
    }
    n = 0;
    B.libraries(LBR) {
      OutputPackages(LBR);
    }

  }
  if (schematic) schematic(SCH) {
    SCH.libraries(LBR) {
      CurrentLbrName = filesetext(EditName, "");
      CreateOneLibHeader(LBR, SCH.name);
      noLbrs = 0;
      break;
    }
    n = 0;
    SCH.libraries(LBR) {
      OutputPackages(LBR);
    }
    n = 0;
    SCH.libraries(LBR) {
      LBR.devices(DEV) {
        OutputSymbols(LBR);
      }
    }
    n = 0;
    SCH.libraries(LBR) {
      LBR.devices(DEV) {
        OutputDevices(LBR);
      }
    }
  }
  if (!noLbrs) {
     cmd += "WRITE;\n";
     CreateTrailer();
  }
  return;
}

// -----------
void show_save_script_file(string cm) {
  ScriptName = WorkPath+PureScriptName;
  output(ScriptName, "wtD") printf("%s", cm);
  return;
}

//------------ main ----------------------------------------
if (library) {
  dlgMessageBox(TR("Please start from schematic or board editor!"));
  exit(1);
}
else {
  if (board) board(B)         EditName = filename(B.name);  // name of loaded board/schematic w/o path
  if (schematic) schematic(S) EditName = filename(S.name);
}

PureScriptName = filesetext(EditName, ".scr");               // name of generated script w/o path
WorkPath = get_project_path();

int Result = dlgDialog(TR("Export EAGLE libraries from drawing")) {
    dlgHBoxLayout {
      dlgLabel("&" + TR("Path") + ":");
      dlgStringEdit(WorkPath);
      dlgPushButton(TR("&Browse") + "...") {
        if (cmd) cmd = "";
        h = WorkPath;
        WorkPath = dlgDirectory("", WorkPath);
        if (WorkPath == "") WorkPath = h;
        else WorkPath += "/";     // 29.03.2005 support@cadsoft.de
      }
    }
    dlgGroup(TR("Creation mode")) {
      dlgRadioButton("&" + TR("Export original libraries"), Merge); //name like drawing
      dlgRadioButton("&" + TR("Merge to one library, ") +       TR("original library names as prefix for symbol, package and device names"), Merge);
      dlgRadioButton(      TR("Merge to one library, ") + "&" + TR("unchanged symbol, package and device names"), Merge);
    }
    dlgLabel(Status, 1);
    dlgHBoxLayout {
      dlgStretch(1);
      dlgPushButton("&" + TR("Help") + "...")  DisplayHelp();
      dlgPushButton("+&" + TR("OK")) {
        cmd = "";
        if (Merge > 0)
           make_one_lbr();
        else
           make_lbr();
        show_save_script_file(cmd);
        exit("SCRIPT '"+ScriptName+"';\n");
      }
      dlgPushButton("-&" + TR("Cancel")) dlgReject();
    }
};
